Confusing java code - related to static binding - java

something about this code confuses me. The first print line is 1600. I understand it has something to do with the static class being Car and not Sportscar. But we created the object as a Sportscar, so why isn't the volume field 3500? Thanks.
public class Car {
public int volume;
public Car() { this(1600); }
public Car(int volume) { this.volume = volume; }
public String toString() { return "Car:" + volume; }
}
public class SportsCar extends Car {
public int volume;
SportsCar() { this(3000); }
SportsCar(int volume) { this.volume = volume; }
public String toString() {return "SportsCar:"+volume;}
}
public class CarApplication {
public static void main(String [] args) {
Car car = new SportsCar(3500);
System.out.println(car.volume);
System.out.println(car.toString());
System.out.println(car);
}
}

First off, note that when you call the constructor for SportsCar Java will automatically call the default constructor for its parent class, Car. This will set the volume field of the object's parent Car class to 1600.
In Java, there is no polymorphism for fields. So whereas the toString() method inside your SportsCar class always overrides that of its parent (Car) class, the rules for how identically named instance variables are accessed is a little different.
If you are accessing volume from within the SportsCar class then the volume of SportsCar will be used. From outside the class itself (so when you're calling from CarApplication) which instance variable is accessed depends upon the compile-time type of the object in question. Because you declare the type of car to be a Car, the volume value of the parent Car class is used--hence 1600 is printed. If you had instead declared car to be a SportsCar, 3500 would be printed.
Another option would be this:
System.out.println(((SportsCar)car).toString());
This will output 3500, since the type of car has been cast to SportsCar.

So the point here is that when you are using car.volume, car variable is of type Car and object reference is of type SportsCar.
As both the classes have the same element named volume and you are trying to refer it through parent object variable, it is returning 1600.
If you will typecast and then check the volume, it will return 3500 as per below code:
System.out.println(((SportsCar)car).volume);

When you declare something in this way:
ParentClass variable = new ChildClass(Args);
The methods and fields in ParentClass are the only ones available to you. It is restricted because you declared the type as a ParentClass. Because Car's volume is 1600 and the object is restricted to the ParentClass's methods and fields, it prints 1600.
An example would be as follows:
Consider I had an Apple Class and a Fruit class:
public class Fruit {
private String type;
public Fruit(String type) {
this.type = type;
}
public String getType() {
return type;
}
}
And the Apple Class:
public class Apple extends Fruit {
private String variant;
public Apple(String variant) {
System.out.println("I like " + variant + apples too!");
super("Apple");
}
public String getVariant() {
return variant;
}
}
And now I instantiate like this:
Fruit ap = new Apple("Fiji");
I don't have access to getVariant() or any methods in the Apple class because the type is of the parent class, not of Apple. I would be able to do getType() but that's it.
In your case:
public class SportsCar extends Car {
public int volume;
SportsCar() {
this(3000);
}
SportsCar(int volume) {
this.volume = volume;
}
public String toString() {
return "SportsCar:"+volume;
}
}
Although SportsCar has it's own constructor and takes it's own volume, the parent class Car is the actual type, causing the Car constructor to be called, hence setting volume to 1600. To specify a SportsCar object, do this:
SportsCar sc = new SportsCar(3500);
System.out.print(sc.toString());
This will print out:
SportsCar:3500
You can also typecast it like so:
Car c = new SportsCar(1600);
System.out.print(((SportsCar) c).toString());

Aman Chhabra answer is right. You created a SportCar object which is from the Car "family". And what you are printing is the volume of Car, not the volume of SportCar.
One other way is to create a SportsCar object instead of Car.
P.s: you should always set your class attributes as private :)

Related

Polymorphism - Method overriding and overloading not clear

I am learning polymorphism but not able to understand how it works
When I try to create an object Car car = new Audi();, overriding prints "This is Audi". But with same object I am not able to call first(int x, int y) of Audi class... I am able to call only Car class first().
Overriding prints the Audi method, Overloading prints Car method.. when calling with same object..
Class Car
class Car {
public void carName() {
System.out.println("Parent of car");
}
public int first() {
System.out.println("Base - Parent");
return 2;
}
}
Class BMW
class BMW extends Car {
public void carName() {
System.out.println("This is BMW");
}
public int first(int x) {
System.out.println("BMW override");
return x;
}
}
Class Audi
class Audi extends BMW {
public void carName() {
System.out.println("This is Audi");
}
public int first(int x, int y) {
System.out.println("AUdi Override");
return x;
}
}
Class PloyMor
public class PolyMor extends Audi {
public static void main(String args[]) {
Car car = new Audi();
car.carName();
car.first();
}
}
Important: I am assuming that class BTW extends Car not Audi (which would makes no sense IMO).
But with same object i am not able to call first(int x, int y) of Audi class.
You need to distinguish between variable type and value (object) type. In case of
Car car = new Audi();
variable type is Car while type of object it holds is Audi.
You need to realize that compiler doesn't assume what is the value of variable. It applies same rules as if it was parameter of method like
void someMethod(Car car){
//do something with `car`
}
where inside of that method we don't know if it will be used with someMethod(new Audi()); or someMethod(new BMW());.
So which methods can be safely invoked via car variable inside someMethod body? Only those which are guaranteed to be implemented (to appear) in all objects which can be used as method arguments. If that method would let us write car.first(1, 2); it will work for scenario like someMethod(new Audi()) but will fail for someMethod(new BMW()) because BMW doesn't have first(int x, int y) method.
It would have worked if you had another method defined in the base class like follows:
public int first(int a, int b) {
System.out.println("Base - Parent");
return 2;
}
As the final object you are trying to create is an object of class Car, it only has the methods from the Car class which if are present in Audi class, will override the methods in Car class.
Calling a method depends on the compile-type rather than the runtime type.
Car car = new Audi();
^ ^
compile-type runtime type
Compiled Type vs Runtime Type:
Source
Credit: #duffymo
Let's say we have
A test = new B();
at compile time: the compiler only knows that the variable test is of the type A. He does not know that we are actually giving him an instance of B. Therefore the compile-type of test is A.
at run time: the type of test is known to be B and therefore has the run time type of B
Since compiler does not know what type will be stored at runtime it only allows us to call methods of Class A
Solution:
If you want to call methods from instantiated type do this
then you will be able to access method first(int x, int y)
Audi car = new Audi();
car.carName();
car.first(); // Original Method
car.first(3, 3); // Overloading
Output:
This is Audi
Base - Parent
AUdi Overload
P.S Like OldProgrammer mentioned Audi should extend Car instead of BMW
You are not overriding first from Car in Audi, because the signatures are different.
The signature includes the method name, parameters, and throws clause.
The method first() from Car takes no arguments.
In Audi it takes two ints as arguments. Not the same.
Even your simple hierarchy is not a good design. You are having problems because your implementation is bad.
Try this:
public abstract class Car {
private String name;
private int id;
public Car(String name, int id) {
this.name = name;
this.id = id;
}
public String getName() { return this.name; }
public int getId() { return this.id; }
public static void main(String [] args) {
List<Car> cars = new ArrayList<>();
cars.add(new Audi());
cars.add(new Bmw());
System.out.println(cars);
}
}
class Audi extends Car {
public Audi() {
super("Audi", 1);
}
}
class Bmw extends Car {
public Bmw() {
super("BMW", 2);
}
}

Set methods in a subclass

I need to set values from the subclass by invoking the set method from the super class. I need to set monster name and health from the subclass
Alien class
public class Alien {
//Instance variables
public String monsterName;
public int HP;
//A method that sets monster properties
public void setValues(String monsterN, int health) {
monsterName = monsterN;
HP = health;
}
//A method that returns the monster name
public String getName() {
return monsterName;
}
//A method that returns monster's health
public int getHP() {
return HP;
}
//ToString method that prints out the info
public String toString() {
return("Monster name: " + getName() + "Monster's current health point: " + getHP());
}
}
Snake class
public class SnakeAlien extends Alien {
//Instance variable
//Set the snake values
Alien aObject = new Alien();
aObject.setvalues("Snake\n" 55));
}
The main class
public class Main {
public static void main(String[] args) {
Alien object = new Alien();
object.toString();
System.out.println(object);
}
}
I know how would I set these values through the main class, but I need to do it from the sub class SnakeAlien which inherets from the Alien class.
Because everything is public (which is terrible BTW) you can simply call the superclass' methods frimbthe subclasses. Not even super is needed.
Though, you should change members you don't want to reach from the outside to at least protected, and if you never want to make a pure Alien object, you should make that an avstract class.

Copy an object that contains a super class of another object

I would like to create a copy of an object that contains a super class of another object. In this example I want to make a copy of the Box that contains a Toy. But all kind of toys can be in the box. What would be the best way to create the copy constructor in Toy?
class Box {
Toy toy;
public Box(Toy toy) {
this.toy = toy;
}
public Box(Box box) {
this.toy = new Toy(box.getToy());
}
}
abstract class Toy {
public Toy(String name) {
// ...
}
}
class Car extends Toy {
public Car(String name) {
super(name);
// ...
}
}
class Puppet extends Toy {
public Puppet(String name) {
super(name);
// ...
}
}
I don't really have an idea how to approach this problem.
Make Toy have an abstract method copy() with return type Toy. Then you will be forced to override this in Car and Puppet. In the copy constructor for Box you can use box.getToy().copy().
You can override the clone method of each Toy's subclass and then :
public Box(Box box) {
this.toy = (Toy) box.getToy().clone();
}
Alternatively, if you have a constant number of types of toy, you can use an enumeration instead of a class.
i think this structure can help you to have an idea,in this case we pass an Object toy using Box Constructor to SuperClass(Toy) and in Toy Class we have a Constructor to Accept an Object from Toy Class then it's call getInstance Method for Initialize toy object(just for example).
class Box extends Toy
{
public Box(Toy toy)
{
super(toy);
}
}
Class Toy
{
private static Toy toys = new Toy();
Toy(){}
Toy(Toy toy)
{
toy = Toy.getInstance();
}
public static Toy getInstance()
{
return toys;
}
}
and either,if you don't want other Classes(sub class) to don't see a specified methods and attributes just make them private,and if you want sub classes haven't access to set and get methods too,make them private only!

Java error creating subclass constructor

I trying to learn some Java and I've got stuck creating a subclass. I keep getting a There is no default constructor available in... error.
This is the code:
class Car
{
String name;
int speed;
int gear;
int drivetrain;
String direction;
String color;
String fuel;
public Car(String carName, int carSpeed, String carDirection, String carColor, int carDriveTrain, int carGear)
{
name = carName;
speed = carSpeed;
gear = carGear;
drivetrain = carDriveTrain;
direction = carDirection;
color = carColor;
fuel = "Gas";
}
void shiftGears(int newGear){gear = newGear; }
void accelerateSpeed(int acceleration){speed = speed + acceleration; }
void applyBrake(int brakingFactor){ speed = speed - brakingFactor;}
void turnWheel(String newDirection){ direction = newDirection; }
}//end of Car class
class Suv extends Car
{
void applyBrake(int brakingFactor)
{
super.applyBrake(brakingFactor);
speed = speed - brakingFactor;
}
}
The issue comes when I try to create the "Suv" subclass. What am I doing wrong? Thanks!
You probably want to create the following constructor in Suv that initialzies that parameters that the Car constructor has:
public Suv(String carName, int carSpeed, String carDirection, String carColor, int carDriveTrain, int carGear)
{
super (carName, carSpeed, carDirection, carColor, carDriveTrain,carGear);
}
The alternative is to add the parameterless constructor to Car, in which case the default constructor of Sub would call that constructor :
public Car()
{
}
Since "Car" has a constructor, any subclass derived from Car needs a constructor as well. So first you need to put a constructor in the Suv class.
Example:
class Suv extends Car {
public Suv() {
super( /* here you need to pass arguments to create a car */);
// any other constructor code
}
}
Add the following constructor to Car:
public Car(){}
The problem is that Suv cannot be created since in order to run Suv's default constructor the constructor of Car needs to be ran first, and Car has only a constructor that accepts arguments so it can't be used as default-constructor.
Another approach would be, as #Markus suggested, to implement a constructor in Suv that'll call super with all the required arguments. Either way, the main idea is that in order to instantiate Suv we need to be able to instantiate Car first otherwise we'll get a compiler error.
What i would suggest is to not put the SUV class in the same file. Instead, Create another class in your current package, name it SUV, extended it and call the superclass constructor by either of the following two syntax:
super(); //calls the superclass no-argument constructor with no parameter list
or
super(parameter list); //calls the superclass constructor with a matching parameter list

Java OOP issue - Related to Interface/Abstract Classes

I'm stuck with a Java OOP problem. I have come up with some toy code to explain the problem. Here are my classes -
Class 1 - Car.java
public class Car {
public void reportProblem(String problem){
ReportUtil.reportVehicleInfo("Car", 4, problem); //4 is number of wheels
}
//bunch of other methods
}
Class 2 - Truck.java
public class Truck {
public void reportProblem(String problem){
ReportUtil.reportVehicleInfo("Truck", 6, problem);
}
//bunch of other methods
}
Class 3 - ReportUtil.java
public class ReportUtil {
public static void reportVehicleInfo(String name, int wheels, String problem){
System.out.println(String.format("%s %s %s", name, wheels, problem));
}
}
Class 4 - Test.java
public class Test {
public static void main(String[] args) {
Car c = new Car();
c.reportProblem("puncture");
Truck t = new Truck();
t.reportProblem("engine missing");
}
}
I want to abstract the "reportProblem" method implementation in "Car" and "Truck" to a parent class. This is what I did -
Class 1 - Vehicle.java
public abstract class Vehicle {
public String mName;
public int mNumWheels;
public void reportProblem(String problem){
ReportUtil.reportVehicleInfo(mName, mNumWheels, problem);
}
public void setName(String name){
mName = name;
}
public void setNumWheels(int numWheels){
mNumWheels=numWheels;
}
}
Class 2 - Car.java
public class Car extends Vehicle {
//bunch of other methods
}
Class 3 - Truck.java
public class Truck extends Vehicle {
//bunch of other methods
}
Class 4 - ReportUtil.java (No change made to this class).
public class ReportUtil {
public static void reportVehicleInfo(String name, int wheels, String problem){
System.out.println(String.format("%s %s %s", name, wheels, problem));
}
}
Class 5 - Test.java
public class Test {
public static void main(String[] args) {
Car c = new Car();
c.setName("Car"); //NOTE : Can be missed!
c.setNumWheels(4); //NOTE : Can be missed!
c.reportProblem("puncture");
Truck t = new Truck();
t.setName("Truck"); //NOTE : Can be missed!
t.setNumWheels(6); //NOTE : Can be missed!
t.reportProblem("engine missing");
}
}
This achieves what I want (I have abstracted the implementation of "reportProblem"). But I know this is not the best way to do it. One reason is that the "reportProblem" method should not be called without calling "setName" and "setNumWheels" methods. Otherwise 'null' will be passed. Is there a way of enforcing, using some OOP technique, the two methods calls (setName and setNumWheels) BEFORE reportProblem is called?
I hope I have made myself clear. If I am not, just let me know how you would have done it so that I can learn from it.
Yes, make name and numWheels final and assign then in the constructor. So...
Class 1 - Vehicle.java
public abstract class Vehicle {
public final String mName;
public final int mNumWheels;
protected Vehicle(String name, int numWheels){
this.mName = name;
this.mNumWheels = numWheels;
}
public void reportProblem(String problem){
ReportUtil.reportVehicleInfo(mName, mNumWheels, problem);
}
...
}
Class 2 - Car.java
public class Car extends Vehicle {
public Car(){
super("Car", 4);
}
//bunch of other methods
}
Class 3 - Truck.java
public class Truck extends Vehicle {
public Truck(){
super("Truck", 6);
}
//bunch of other methods
}
Also, public fields are not good OO practice, because they expose details of your class' implementation that could be modified by users of the class. Those fields should be private. If the clients of the class need to know about them (or change them), then you should allow public getter (or setter) methods.
If you want to set the fields "required", you can set them as parameters in Truck/Car constructors and not provide a default constructor for these classes.
If members are essentials for an object's state/functionality, put them as part of a constructor, so it is not possible to create an object (and call the method of concern) without providing proper values for these members.
But you should not also provide a no-args constructor.
If there are too many parameters needed consider looking into the Builder idion
In addition to #Tony's answer (+1) if you have to use bean notation (default constructor and setters) and still do not want to allow using any business methods before the object is initialized you can do the following.
Define abstract method checkInitalized() in your Vehicle class. Implement this methods for your Car and Truck. BTW this method will probably have default implementation in Vehicle. In this case do not forget to call super from its overridden versions.
checkInitalized() should throw exception (e.g. IllegalStateException) if not all required fields are initialized.
Now call this method in the beginning of each business method. This will prevent you from using object that is not initialized yet.
This technique is a little bit verbose. Probably using wrapper pattern or AOP (e.g. AspectJ) may be useful here.

Categories