Can you change a immutable class? - java

This is a theoretical question for practice.
The question is
Create an immutable class Car.
Create some instances of car to fill an Arraylist<Car> inside a Garage class.
The MyGarage class implements these methods from Garage:
getCar(String reg) – search for the car with registration number reg.
getMake(String make) – returns a list of cars that match the given make.
totalValue() – calculates the total value of all cars in the list.
changeOwner(String reg, String ow) – change the owner of car that has registration number reg to ow.
I do not understand the changeOwner method as it is not suppose to be able to change a instances of a immutable class I thought???
This is what I have done to work around it but just seems silly
import java.util.ArrayList;
public class MyGarage implements Garage {
private ArrayList<Car> myGarage;
public MyGarage() {
myGarage = new ArrayList<Car>();
}
#Override
//Adds a Car if the registration is unique
public boolean add(Car c) {
for(Car car : myGarage) {
if(car.getRegistration().equals(c.getRegistration())) {
System.out.println("Car has the same Registration as another illegal");
return false;
}
}
myGarage.add(new Car(c.getOwner(),c.getRegistration(),c.getMake(),c.getkilometres(), c.getprice()));
return true;
}
#Override
public Car getCar(String carID) {
for(Car car : myGarage) {
if(carID.equals(car.getRegistration())) {
System.out.println("Car Found");
return car;
}
}
System.out.println("No car of that record");
return null;
}
#Override
public ArrayList<Car> getMake(String make) {
ArrayList<Car> carModel = new ArrayList<Car>();
for(Car car : myGarage) {
if (car.getMake().equals(make)) {
carModel.add(car);
}
}
System.out.println(carModel.toString());
return carModel;
}
#Override
public void totalValue() {
double amount = 0;
for(Car car : myGarage) {
amount = car.getprice() + amount;
}
System.out.println("The total amount is: " + amount);
}
#Override
public boolean changeOwner(String registration, String ow) {
for(Car car : myGarage) {
if(car.getRegistration().equals(registration)) {
myGarage.remove(car);
car = new Car(ow, "444","F-50", 4, 4000.99);
myGarage.add(car);
return true;
}
}
return false;
}
}

In object-oriented and functional programming, an immutable object
(unchangeable object) is an object whose state cannot be modified
after it is created. This is in contrast to a mutable object
(changeable object), which can be modified after it is created. In
some cases, an object is considered immutable even if some internally
used attributes change, but the object's state appears unchanging from
an external point of view. - WikiPedia
Immutable objects are thus instances whose state doesn’t change after they have been initialized. These types of classes are generally good for applications that need to implement some form of caching and where you are worried about thread-safety in a multi-threaded environment (immutable objects are inherently thread-safe).
I don't see your Car class, but assuming it'll look something like this:
public final class Car {
final String registration;
final String owner;
public Car(String registration, String owner) {
this.registration = registration;
this.owner= owner;
}
public String getRegistration() {
return registration;
}
public String getOwner() {
return owner;
}
}
... notice that there are no setter methods in this class. Hence a car can only be initialized (i.e Car myCar = new Car("abcd", "John"); and the variables in them (namely, registration and owner) can never be updated.
So your changeOwner method is essentially looping through the instances of car in your garage and when it finds a matching registration number it removes that instance of car from your garage and then adds a whole new one.
To demonstrate this, you can run the following:
public class Garage {
public static void main(String ... args) {
List<Car> myGarage = new ArrayList<>();
myGarage.add(new Car("CG404GH", "John"));
System.out.println(myGarage);
for(Car car : myGarage) {
if("CG404GH".equals(car.getRegistration())) {
myGarage.remove(car);
Car updateCar = new Car("DD404GH", "John");
myGarage.add(updateCar);
}
}
System.out.println(myGarage);
}
}
This would print out something similar to the following (the portion after the # would be different on each run):
[Car#4411d970]
[Car#6442b0a6]
The important thing to notice here is that the value after the # are different, hence they are two completely different classes (instances) of car

Related

java 8 Check for duplicates and only then add to an ArrayList

I have created a test class below. My main objective is to add an object to a given list only if the object's certain parameters (in this case name) do not match.
import java.util.ArrayList;
import java.util.List;
public class SmartAuto {
static Car car1 = new Car(1, "Lexus");
static Car car2 = new Car(2, "Tesla");
static Car car3 = new Car(3, "Lexus");
public static void main(String[] args) {
List<Car> cars = new ArrayList<Car>();
addNewCar(cars, car1);
addNewCar(cars, car2);
addNewCar(cars, car3);
System.out.println(cars.size());
}
private static List<Car> addNewCar(List<Car> cars, Car car) {
if (cars.size() == 0) {
cars.add(car);
return cars;
}
cars.forEach(c -> {
if(c.name == car.name) {
System.out.println("Can't add as car names are duplicate");
return;
} else {
cars.add(car);
return;
}
});
return cars;
}
public static class Car {
int id;
String name;
Car (int id,String name) {
this.id = id;
this.name = name;
}
}
My problem is this is failing with the below exception that I don't understand :
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList.forEach(ArrayList.java:1542)
at Sample/main.SmartAuto.addNewCar(SmartAuto.java:25)
at Sample/main.SmartAuto.main(SmartAuto.java:14)
Also, I need to know if this code can be written in a better way in java 8?
Check for existence before adding; do not add inside the forEach loop (this is causing the exception).
Use .equals to compare String objects, not ==.
You can use Stream#noneMatch for checking the condition. (For better performance, consider storing a Set of names that are already in the List.)
if (cars.stream().noneMatch(c -> c.name.equals(car.name))) cars.add(car);
else System.out.println("Can't add as car names are duplicate");
return cars;
You are not supposed to modify the source list within the Stream's forEach.
The behavior of this method is unspecified if the action performs side-effects that modify the underlying source of elements, unless an overriding class has specified a concurrent modification policy.
Change your addNewCar method to identify if a duplicate exists first by looping through to the end of the list or exiting out when one exists.. and then add to the list. BTW.. a cleaner way you can accomplish this is by changing the collection to a Set and defining the Car object hashcode to be the same as name field hashcode and adding to a HashSet which then automatically stores unique cars.
But, limiting the answer to a quick fix in your code snippet, the following change to addNewCar should address the problem.
private static List<Car> addNewCar(List<Car> cars, Car car) {
if (cars.size() == 0) {
cars.add(car);
return cars;
}
boolean duplicate = false;
for (Car c : cars) {
if (c.name == car.name) {
System.out.println("Can't add as car names are duplicate");
duplicate = true;
break;
}
}
if (!duplicate) {
cars.add(car);
}
return cars;
}

Accessing properties of Generic class in Java

Hi I am new to Java Generics and I am trying to access properties inside generic class. So far I have found C# related answers, it would be of great help if someone could please guide as to how I should do it in java.
Say I have Class Car and it has properties integer type wheels and String type model.
Now I have a method say vehiclehelper(List<T> vehicles) which takes a generic type list.
At place of invocation of vehiclehelper, it will be like :
List<Car> cars = new ArrayList<>();
cars.add(new Car(4, "Honda"));
vehiclehelper(cars);
Now coming to vehiclehelpermethod, in its body I want to loop over the list<T>vehicles and fetch the wheels property from it..something like:
for(T vehicle: vehicles)
count+ = vehicle.getWheels();
Now here at this point I am getting error, saying property isn't defined. What should I do to fetch the wheel property from my list of generic type?
create an interface ICar or an abstract class that represents a car, and has the methods that you expect a car to have, (e.g getWheels()).
then send a list of the objects that implements/extend this class as a parameter carHelper(List<ICar> cars)
Your class Car is not generalized. It's common class. You just manipulate Car objects via generalized List collection. It should not any issues with access to Car fields through getters or setters if that fields are private. Code below works perfectly.
public class App {
public static void main(String[] args) {
List<Car> carList = new ArrayList<>();
carList.add(new Car(4, "Honda"));
carList.add(new Car(3, "Polaris Slingshot"));
System.out.println(carHelper(carList)); // the result is 7
}
static int carHelper(List<Car> cars) {
int count = 0;
for (Car car : cars) {
count += car.getWheel();
}
return count;
}
}
class Car {
private int wheels;
private String name;
public Car(int wheels, String name) {
this.wheels = wheels;
this.name = name;
}
public int getWheels() {
return wheels;
}
public void setWheels(int wheels) {
this.wheels = wheels;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
If you want to have universal method carHelper for any object type, then you have to make it generalized:
static <T> int carHelper(List<T> elements) {
int count = 0;
for (T element : elements) {
count += element.getWheel();
}
return count;
}
But in the case you will get compile-time error at element.getWheel() because T type is some unknown type. Compiler does not know about his methods. To find some generic solution we have to define boundary for method parameters. Let's say input parameters will be Vehicle or it's children. In Vehicle class, let's say, we will introduce needed fields and his descendants will inherit them. In the case below listed method will work. Read about wildcards in generics.
static int carHelper(List<? extends Car> elements) {
int count = 0;
for (Car element : elements) {
count += element.getWheels();
}
return count;
}

Instantiate objects using a helper class

I want to create an instance of my object, car. The problem I have is that i can create an instance of the car object such as, Car car1 = new car("Audi","A4","BF10YMR"); however I want to create car objects through a helper class. How do I call this helper class in main so that is of type car and not of type carHelper?
The car object requires a random registration number to be created and this is created in the carHelper class. The object is returned.
public class Car implements Comparable<Car>
{
public class Car
{
private String make;
private String model;
private String registration;
public Car(String make, String model, String reg)
{
this.make= make;
this.model= model;
registration = reg;
}
}
public class carHelper
{
public car genCar()
{
String reg = //some method to generate random registration.
String Make = //some method to randomly pick make from a list
String model = //some method to randomly pick model from a list
return new Car(make,model,registration);
}
}
public class Garage
{
public static void main (String args[])
{
Garage MyGarage = new Garage();
Car car1 = new Car("Audi","A4","BF10YMR") //works, but doesn't use helper
Car car2 = carHelper.genCar(); // something like this?
carHelper c = new carHelper(); // thought something like this but
System.out.println(c.genCar()); // creates object of type carHelper
// not car.
MyGarage.add(car1);
MyGarage.add(car2); // gives me carHelper cannot be converted to Car
}
}
public class GarageOp implements CarList
{
public GarageOp()
{
list = new ArrayList<Car>();
}
public boolean add(Car car)
{
if (list.contains(car) == false)
{
list.add(car);
return true;
}
}
}
Expected result is create car object using the helper class and add it to an ArrayList.
You could create this lists in the CarHelper and than, randomly, select the values and create a new Car with them. The UUID creates a random 128 bits (including hex) number and converts to a String
public class CarHelper {
private List<String> makeList = Arrays.asList("s", "t", "f", "n");
private List<String> modelList = Arrays.asList("yyt", "32g", "dc3", "aas");
public Car genCar() {
String reg = UUID.randomUUID().toString();
String make = makeList.get(new Random().nextInt(makeList.size() - 1));
String model = modelList.get(new Random().nextInt(modelList.size() - 1));
return new Car(make,model,reg);
}
}
Make the genCar() method as Static in the CarHelper class.
public car static genCar(){
// do stuff to create object
}
a non-static method can access a static variable or call a static method in Java.

I have problems returning an object from parent ArrayList

I have 4 classes ( I will shorten them for convenience).
public class PoliceStation{
public String name;
ArrayList<Owner> owners = new ArrayList<>();
public boolean addOwner(Owner owner) {
if(findOwnerID(owner)) {
this.owners.add(owner);
System.out.println("Owner "+owner.getName()+" added.");
return true;
}
System.out.println("Owner "+owner.getName()+" with "+owner.getOwnerIDNumber()+" not added.");
return false;
}
and a few more classes for finding owner objects.
And Owner class:
import java.util.ArrayList;
public class Owner {
String name;
String dateOfBirth;
long ownerIDNumber;
String address;
ArrayList<Vehicle> vehicles=new ArrayList<Vehicle>();
ArrayList<Fine> penalties = new ArrayList<Fine>();
public Vehicle findVehicleObject(String plateNum) {
for(int i=0;i<vehicles.size();i++) {
System.out.println(i);
if(vehicles.get(i).getPlateNumber().equalsIgnoreCase(plateNum)) {
System.out.println("Vehicle with that plate number "+plateNum+" exists.");
return vehicles.get(i);
}
}
System.out.println("Vehicle doesnt exist with "+plateNum);
return null;
}
Which consists of addVehicle methods, findVehicle etc.
Vehicle class:
public class Vehicle extends Owner{
PoliceStation ps = new PoliceStation("center");
String plateNumber;
String name;
String type;
//String serialNum;
public Vehicle(String driverName, String dateOfBirth,long ownerID, String address,String plateNumber, String name, String type) {
super(driverName,dateOfBirth,ownerID,address);
this.plateNumber = plateNumber;
this.name = name;
this.type = type;
}
With findVehicle method that should return the vehicle object with the vehicle's plate number:
public Vehicle findVehicle1(String plateNum) {
if(this.plateNumber==plateNum) {
System.out.println("Lookin "+plateNum);
return super.findVehicleObject(plateNum);
}else return null;
}
And after that I have a fourth class called RadioCam that reads a plateNumber from a car: (note: the following code is badly written because I've been trying to get this to work without success)
class RadioCam{
public void detection(double speed) {
System.out.println("Vehicle detected.");
//speed would be a variable that the radioCam would return using radiowaves and doppler effect
if(speed>50) {
String plateNumber = takePicture();
Vehicle veh = new Vehicle(plateNumber);
veh = veh.findVehicle1(plateNumber);//<-- null
System.out.println("-------------------"+veh.getName());//<- null here as well
Owner ownerFine = ps.getOwner(veh);
ownerFine= ps.getOwner(veh);
System.out.println("sssssss"+ownerFine.getName());
//ownerFine = PoliceStation.getOwner(veh);
ps.addFine(ownerFine, "speed violation", veh);//<- so this method doesnt work in the end becuse ownerFine and veh are null
Which returns null to veh = veh.findVehicle1(plateNumber);. And going back to Owner class at public Vehicle findVehicleObject(String plateNum) {
for(int i=0;i<vehicles.size();i++) { <-- vehicle.size() is 0 so it doesn't go through the vehicles at all.
I get it that I need an owner object from the arraylist to get to the vehicle object from the arraylist, but in this case I need a vehicle object from plateNumber (which is a part of the Vehicle class) and from the vehicle object to get the owner object that owns the vehicle object.
I hope I explained my problems good enough.
Try equals method to compare values, this.plateNumber==plateNum may be false even they have same value.
public Vehicle findVehicle1(String plateNum) {
if(this.plateNumber.equals(plateNum)) {
System.out.println("Lookin "+plateNum);
return super.findVehicleObject(plateNum);
}else return null;
}
In your findVehicle1(String plateNum) method you are comparing two strings with the == operator, while you should be using string.equals(Object other)
Try changing
if(this.plateNumber==plateNum) {
to
if(this.plateNumber.equals(plateNum)) {
Also when constructing your vehicle object, you're ommiting a lot of the needed parameters
public Vehicle(String driverName, String dateOfBirth,long ownerID, String address,String plateNumber, String name, String type) {
^ the constructor asks for 7 parameters, while you only give
new Vehicle(plateNumber);
one parameter. Therefore of course your veh.getName() method will return null, because it's an attribute that you did not maintain when you created your vehicle object.
After fixing those issues, you can think about your general code structure, like where to implement loops and such.
I don't know whether or not you use an IDE, but if you do please familiarize yourself with the the debugging functionality to follow the code execution step-by-step in called methods. You could have easily pinpointed the lines of code that act in ways they shouldn't act (after all you already found the lines where null is the result of your method calls).

Temporary extending existing object in Java - is this a good idea?

I'm implementing an algorithm during which we must temporary order existing objects (we will compare them according to this order during execution of the algorithm). I'm thinking of the best way of doing that, while being consistent with OOP paradigm.
So let's think about the following example. We have objects of class Car, and now we want to use the algorithm on such objects. So I thought of making a subclass OrderedCar, which will have a unique int in its fields. This class would have a function - ArrayList<OrderedCar> defineOrder(ArrayList<Car> order), which would output OrderedCar list with numbers corresponding to the indices of a given car in order table. We would then be able to compare OrderedCars using numbers with which they were initialized. We could then execute the algorithm on OrderedCars and convert them to Cars after the algorithm terminates.
During the algorithm I need all of the methods from Car class, that's why I thought of making OrderedCar a subclass. Is this a good idea though? Also, how to create a constructor in Java, which will "copy" the Car and assign a number to it (I'm thinking of something like public OrderedCar(Car c, int order), but what to do with the car c in order to "copy" it? I don't want to copy all of the fields individually, is there some kind of shortcut in Java?)
You could favor composition over inheritance and just create a class that contains a Car and its order:
public class OrderedCar{
private int order;
private Car car;
public OrderedCar(int order, Car car){
this.order = order;
this.car = car;
}
public int getOrder(){
return order;
}
public Car getCar(){
return car;
}
}
Then you can sort this class by its order, and whenever you needed to operate on its car, you would just call the getCar() function.
If it is enough for you to compute the order basing on public methods of your Car class I think the simplest solution would be to just implement a comparator.
Here is a small example. I'm using the age property to do the sorting.
public class Car {
protected int age;
protected String manufacturer;
Car(int age, String manufacturer) {
this.age = age;
this.manufacturer = manufacturer;
}
public int getAge() {
return age;
}
public String toString() {
return this.manufacturer + " age: " + this.age;
}
}
import java.util.Comparator;
public class CarComparator implements Comparator<Car> {
public int compare(Car c1, Car c2) {
// do some complicated comparison using
// Car public methods, we simple use to
// age property here
return c1.getAge() - c2.getAge();
}
}
import java.util.ArrayList;
public class CarComparingApp {
public static void main(String[] args) {
Car ferrari = new Car(1, "Ferrari");
Car maserati = new Car(4, "Maserati");
Car subaru = new Car(3, "Subaru");
ArrayList<Car> cars = new ArrayList<Car>();
cars.add(ferrari);
cars.add(maserati);
cars.add(subaru);
cars.sort(new CarComparator());
for(Car c: cars) {
System.out.println(c);
}
}
}

Categories

Resources