Abstract class and its subclasses [duplicate] - java

Basically what the title says, but some elaboration. I have a SuperClass with a couple of SubClasses. I needed an ArrayList to hold both types of Subclasses so hence the ArrayList of type SuperClass. I tried to access Subclass1's getQuantity() method using ArrayList.get(0).getQuantity(); (assuming that index 0 is of type SubClass1). I get the error: getQuantity is undefined for the type SuperClass.
Do the SubClass objects not keep their properties when put into a SuperClass ArrayList? And if they do keep their properties, how do I access them?

The objects themselves are still a subclass, but when you get them out of the collection it only knows about the superclass so it can't tell you which is which, it has to pick the common denominator.
If you know exactly that a specific index holds an object of type Subclass you can just cast it:
Subclass myObject = (Subclass) list.get(0);
System.out.println(myObject.getQuantity());
And it should work.
And a safer way requires testing if the object is really what you think it is:
SuperClass myObject = list.get(0);
if ( myObject instanceof Subclass) {
Subclass mySubObject = (Subclass) myObject;
System.out.println(mySubObject.getQuantity());
}
The first example raises an exception if the object is not of type Subclass, the second one wouldn't since it tests before to make sure.
What you need to understand here is that SuperClass myObject = list.get(0) is not the object itself, but a reference to access the object in memory. Think about it as a remote that allows you to control your object, in this case, it's not a fully featured remote, since it doesn't show you all your object can do, so you can switch to a better one (as in Subclass myObject = (Subclass) list.get(0)) to be able to access all features.
I'd surely recommend the Head First Java book as it covers this stuff in great detail (and I stole this remote example from there).

All of the objects retain their own class identity, but the code that uses the ArrayList isn't directly aware of it. As far as it's concerned, the ArrayList only holds references to SuperClass-type objects, and it can only call SuperClass's methods on objects it retrieves from it.
The calling code can use instanceof or similar techniques to find out if a particular object in a collection is of a subtype, but this is usually bad practice, since it usually indicates mixing of the different levels of abstraction. The one case where this is generally considered reasonable is if the subclass has some optional high-performance characteristic that the caller can take advantage of (and that measurement has determined is worth complicating the code for); one example might be that while List's get() method is has no performance guarantees, some implementations, like ArrayList, also implement RandomAccess, which indicates that there's no performance penalty to using get() in any order.

When you have some ArrayList and you fill it with things that extend SuperClass you have to check instanceof and cast to get to the methods specific to those subclasses.
Example:
ArrayList<Animal> animals = new ArrayList<Animal>;
animals.add(new Duck());
animals.add(new Cow());
animals.add(new Horse());
for (Animal animal : animals)
if (animal instanceof Horse) {
Horse horse = (Horse)animal;
horse.gallop();
}

Related

What is the appropriate instance declaration of a subclasses?

I have a class called A (the parent) and class B (subclass of A)
My question is what is the difference between the next lines :
B b1 = new B();
A b2 = new B();
I know that variables and methods in class B cannot be accessed in case of object b2
But how it works in memory ? what is the point of this? when to use each of them ?
One reason to use the superclass as the variable type:
Suppose you have an Animal class, and several subclasses like Elephant, Tiger, Giraffe, Hippo, etc.
Now you have another class called Zoo, which contains an Array called animals. Then you could have:
animals[0] = new Tiger();
anumals[1] = new Elephant();
But it is better to have animals declared as an ArrayList that can grow or shrink:
animals.add( new Tiger() );
animals.add( new Elephant() );
animals.add( new Hippo() );
If a subclass has a method that is not an overload of a parent method, you can still access the subclass methods via the superclass variable by casting it:
Animal a = animals.get(index);
if ( a instanceof Tiger ) {
((Tiger)a).tigerMethod( ... );
}
Not that this type of thing might not be the best design, but it illustrates the point.
Object can have multiple Interfaces, let's say class B inherits from class A:
Class A can have 5 public methods,
Class B have additionaly 5 own public methods,
so in class B you see all 10 methods, whereas using class A you see only 5.
This is important, because in this way you can control which part (of the interface) of the class you give to other's (other programmers to use).
For egzample:
If you return from your public method a List<>, like:
public List<SomeClass> giveMeSomeList() {
// both return statements are perfectly valid as return type is List<>
return new LinkedList<SomeClass>();
// return new ArrayList<SomeClass>();
}
then you are allowed to use any implementation of the List<> you have eg. You can chnage the implementation, that you construct and return an ArrayList<> or LinkedList<> from your method, but since the declared return type is List<> it makes no harm, no one will rely upon that you return concrete implementation (at least your'e not responsible for that), just that it will be a kind of List<>. But if you return from your public method very concrete implementation of a class, then your give others invitation to use and rely upon this concrete implementation, which in turn block the methos from the ability to chanage the underlying implementation without doing harm to other code.
So in this way (using apropriate types) you can make some restrictions on other programmers as to which methods they are allowed to use (the interface), and give you some freedom to change the implementation in the future - like in the exmple with lists.
When you use an interface in an method arguments type, you make the method more general, as it can be used with any subtype of the interface (or type).
Sometimes you just need very concrete implementation as an argument to your method, because without that you cannot do the needed operation, so you will rely on very concrete type to be supplied to your method, and declare that type in the arguments of the method.

Java Collection.contains() not generic [duplicate]

This question already has answers here:
What are the reasons why Map.get(Object key) is not (fully) generic
(11 answers)
Closed 6 months ago.
Why isn't Collection.remove(Object o) generic?
Seems like Collection<E> could have boolean remove(E o);
Then, when you accidentally try to remove (for example) Set<String> instead of each individual String from a Collection<String>, it would be a compile time error instead of a debugging problem later.
remove() (in Map as well as in Collection) is not generic because you should be able to pass in any type of object to remove(). The object removed does not have to be the same type as the object that you pass in to remove(); it only requires that they be equal. From the specification of remove(), remove(o) removes the object e such that (o==null ? e==null : o.equals(e)) is true. Note that there is nothing requiring o and e to be the same type. This follows from the fact that the equals() method takes in an Object as parameter, not just the same type as the object.
Although, it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, that is certainly not always the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So coming back to the example in this question, it is possible to have a Map<ArrayList, Something> and for me to call remove() with a LinkedList as argument, and it should remove the key which is a list with the same contents. This would not be possible if remove() were generic and restricted its argument type.
Josh Bloch and Bill Pugh refer to this issue in Java Puzzlers IV: The
Phantom Reference Menace, Attack of the Clone, and Revenge of The
Shift.
Josh Bloch says (6:41) that they attempted to generify the get method
of Map, remove method and some other, but "it simply didn't work".
There are too many reasonable programs that could not be generified if
you only allow the generic type of the collection as parameter type.
The example given by him is an intersection of a List of Numbers and a
List of Longs.
Because if your type parameter is a wildcard, you can't use a generic remove method.
I seem to recall running into this question with Map's get(Object) method. The get method in this case isn't generic, though it should reasonably expect to be passed an object of the same type as the first type parameter. I realized that if you're passing around Maps with a wildcard as the first type parameter, then there's no way to get an element out of the Map with that method, if that argument was generic. Wildcard arguments can't really be satisfied, because the compiler can't guarantee that the type is correct. I speculate that the reason add is generic is that you're expected to guarantee that the type is correct before adding it to the collection. However, when removing an object, if the type is incorrect then it won't match anything anyway. If the argument were a wildcard the method would simply be unusable, even though you may have an object which you can GUARANTEE belongs to that collection, because you just got a reference to it in the previous line....
I probably didn't explain it very well, but it seems logical enough to me.
In addition to the other answers, there is another reason why the method should accept an Object, which is predicates. Consider the following sample:
class Person {
public String name;
// override equals()
}
class Employee extends Person {
public String company;
// override equals()
}
class Developer extends Employee {
public int yearsOfExperience;
// override equals()
}
class Test {
public static void main(String[] args) {
Collection<? extends Person> people = new ArrayList<Employee>();
// ...
// to remove the first employee with a specific name:
people.remove(new Person(someName1));
// to remove the first developer that matches some criteria:
people.remove(new Developer(someName2, someCompany, 10));
// to remove the first employee who is either
// a developer or an employee of someCompany:
people.remove(new Object() {
public boolean equals(Object employee) {
return employee instanceof Developer
|| ((Employee) employee).company.equals(someCompany);
}});
}
}
The point is that the object being passed to the remove method is responsible for defining the equals method. Building predicates becomes very simple this way.
Assume one has a collection of Cat, and some object references of types Animal, Cat, SiameseCat, and Dog. Asking the collection whether it contains the object referred to by the Cat or SiameseCat reference seems reasonable. Asking whether it contains the object referred to by the Animal reference may seem dodgy, but it's still perfectly reasonable. The object in question might, after all, be a Cat, and might appear in the collection.
Further, even if the object happens to be something other than a Cat, there's no problem saying whether it appears in the collection--simply answer "no, it doesn't". A "lookup-style" collection of some type should be able to meaningfully accept reference of any supertype and determine whether the object exists within the collection. If the passed-in object reference is of an unrelated type, there's no way the collection could possibly contain it, so the query is in some sense not meaningful (it will always answer "no"). Nonetheless, since there isn't any way to restrict parameters to being subtypes or supertypes, it's most practical to simply accept any type and answer "no" for any objects whose type is unrelated to that of the collection.
I always figured this was because remove() has no reason to care what type of object you give it. It's easy enough, regardless, to check if that object is one of the ones the Collection contains, since it can call equals() on anything. It's necessary to check type on add() to ensure that it only contains objects of that type.
It was a compromise. Both approaches have their advantage:
remove(Object o)
is more flexible. For example it allows to iterate through a list of numbers and remove them from a list of longs.
code that uses this flexibility can be more easily generified
remove(E e) brings more type safety to what most programs want to do by detecting subtle bugs at compile time, like mistakenly trying to remove an integer from a list of shorts.
Backwards compatibility was always a major goal when evolving the Java API, therefore remove(Object o) was chosen because it made generifying existing code easier. If backwards compatibility had NOT been an issue, I'm guessing the designers would have chosen remove(E e).
Remove is not a generic method so that existing code using a non-generic collection will still compile and still have the same behavior.
See http://www.ibm.com/developerworks/java/library/j-jtp01255.html for details.
Edit: A commenter asks why the add method is generic. [...removed my explanation...] Second commenter answered the question from firebird84 much better than me.
Another reason is because of interfaces. Here is an example to show it :
public interface A {}
public interface B {}
public class MyClass implements A, B {}
public static void main(String[] args) {
Collection<A> collection = new ArrayList<>();
MyClass item = new MyClass();
collection.add(item); // works fine
B b = item; // valid
collection.remove(b); /* It works because the remove method accepts an Object. If it was generic, this would not work */
}
Because it would break existing (pre-Java5) code. e.g.,
Set stringSet = new HashSet();
// do some stuff...
Object o = "foobar";
stringSet.remove(o);
Now you might say the above code is wrong, but suppose that o came from a heterogeneous set of objects (i.e., it contained strings, number, objects, etc.). You want to remove all the matches, which was legal because remove would just ignore the non-strings because they were non-equal. But if you make it remove(String o), that no longer works.

Multiple Types of Objects in Java containers

I am learning about generics in Java, and was wondering if this would be considered poor coding.
If I declare an ArrayList<Object> I know that I can put any type of object into this list, as all objects descend from Object. I also know that when I call ArrayList.remove(index) the object that gets taken out of the array is of type Object, and that I need to typecast it to the type of object that I want to use.
Now suppose I have object of Dog, Cat, and Car. Would it be bad to put all three objects into the array, considering they are not similar classes?
Let us revise the definition of a Collection, as found in the Java Introduction to Collections tutorial:
A collection — sometimes called a container — is simply an object that groups multiple elements into a single unit. Collections are used to store, retrieve, manipulate, and communicate aggregate data. Typically, they represent data items that form a natural group [...].
Look at the bold words. That should give you your answer.
If you put objects in ArrayList which do not belong to same implementations, you are definitely inviting trouble in situations when you want to retrieve objects from the list and typecast them to appropriate types. So you should AVOID doing that.
Consider you store objects of Dog and Car in ArrayList.
Then for each object stored, you want to call some method, say barkLoudly(). Now,this method will work fine with the Dog object. But when this is called on Car, boooom....An Exception will arise.
I think the answer is "usually, but not always." There's not a lot of hard and fast rules in coding; if you look hard enough you can always find an exception. But if you pull objects out of a collection that you then have to cast to the correct type, that's usually a design error.
Try to make code so that the objects you work with already have the correct type for what you want to do. If you don't need to cast, then Object can be fine. If you do need to cast, you should probably rethink your design.
The design will be really bad, to avoid all such kind of issues, Generics were introduced.
But the same you can achive using below, provided you have same type of objects.
Cat and Dog extends Animal
Car and Bike extends Vehicle
But do not mix the the above two statements.
Animal class:
public abstract class Animal {
protected String name;
}
Dog class:
public class Dog extends Animal {
public Dog(String name) {
this.name=name;
}
}
Cat class:
public class Cat extends Animal {
public Cat(String name) {
this.name=name;
}
}
Main class:
public class Main {
public static void main(String[] args) {
Cat Cat = new Cat("C");
Dog Dog = new Dog("D");
ArrayList<Animal> list = new ArrayList<Animal>();
list.add(Cat);
list.add(Dog);
}
}
You shouldn't do this
Lets look at it this way, if an we call a generic Object just a "thing" and you create a(n) (Array) List of just "things" then how useful is that to you? To store those three things that have seemingly nothing in common in one list doesn't make sense. Containers were added so that programmers could store groups of similar things.
If we take a look at it from more of a programming point of view as opposed to a real world point of view it raises more problems. Your code might try to perform an action that only Car can do, the compiler doesn't like that your Dog class just tried to turnOnEngine(), and throws an error that you didn't expect.
So in short, it is bad practice to add unrelated items to a collection.
The purpose of generics is to have the compiler doing all the type checking for us. So that we don't need to worry about casting or have to remember what types are really in the list. So using generics also helps documenting your code by making the contained type more explicit.
You could:
not use generics and enforce your own type checking (by safe downcasting with instanceof)
use generics with separate lists for each type
use generics with an abstraction for related class, for example: List<Animal>
It depends on what you want to use the List for.
You should declare the generic type value of the List to be the most restrictive that suits your needs, and that way you get the strongest check from the compiler to eliminate the errors at compile time.
There might be cases when you really just want to store any type of objects in a List in which case you would use List<Object>. Again, depends on what you want to use the List and the elements for.

Java - Object Type Casting and Inheritance

I have superclass A, which is extended by subclasses B1 and B2. Then, I have five subclasses (C1, C2, C3, C4, C5) that extend either B1 or B2.
I am trying to make an array containing one of each of these five subclasses.
These objects are all instantiated as instances of type A.
ClassA[] objects = new ClassA[5];
I attempt to reassign each of the objects to one of the subclasses:
objects[0] = new ClassC1;
objects[1] = new ClassC2; // etc...
At this point, any methods that existed in Class A work fine, but methods defined in B1/B2 or the other subclasses are not found:
objects[0].MethodFromC1(); // returns a "symbol not found" error
The instanceof keyword indicates that objects[0] is an instance of classes A, B1/B2, and C1.
What can I do to maintain my array of class objects (to loop through and perform operations), while getting my code to recognize the methods of the subclasses?
If you are doing a lot of instanceof and conditional logic based on class, you are completely missing out on the benefits of an object-oriented language. Just stick to C.
You should have some method do(), for example, that is abstract in ClassA but implemented in ClassB and ClassC. Then you iterate over the array and call do() on every object in there. The polymorphic call will result in the right do()'s being called.
Hope that helps.
Elements in the objects array don't know anything about ClassC1 since they're only guaranteed to be members of ClassA.
For instance, if you have a class hierarchy of Animal and subclass Cat and its subclass Lion, you're trying to call the Animal.maimSafarigoer() method. Animals in general don't know anything about safarigoers, only Lions know how to do that.
Use the instanceof operator to check if you're operating on a particular subtype.
(Sorry for the gruesome analogy. :-) )
You're trying to implement variants in Java. This subject has long been one of the things I hate the most about this language.
http://jazzjuice.blogspot.com/2010/10/6-things-i-hate-about-java-or-scala-is.html
I have listed about 8 suboptimal ways to do variants there.
You can use the instanceof keyword in the if statement and cast the object to the desired type. For example,
for (ClassA obj : objects) {
// do something common...
if (obj instanceof ClassC1) {
ClassC1 c1Obj = (ClassC1) obj;
c1Obj.MethodFromC1();
}
}

Why aren't Java Collections remove methods generic? [duplicate]

This question already has answers here:
What are the reasons why Map.get(Object key) is not (fully) generic
(11 answers)
Closed 6 months ago.
Why isn't Collection.remove(Object o) generic?
Seems like Collection<E> could have boolean remove(E o);
Then, when you accidentally try to remove (for example) Set<String> instead of each individual String from a Collection<String>, it would be a compile time error instead of a debugging problem later.
remove() (in Map as well as in Collection) is not generic because you should be able to pass in any type of object to remove(). The object removed does not have to be the same type as the object that you pass in to remove(); it only requires that they be equal. From the specification of remove(), remove(o) removes the object e such that (o==null ? e==null : o.equals(e)) is true. Note that there is nothing requiring o and e to be the same type. This follows from the fact that the equals() method takes in an Object as parameter, not just the same type as the object.
Although, it may be commonly true that many classes have equals() defined so that its objects can only be equal to objects of its own class, that is certainly not always the case. For example, the specification for List.equals() says that two List objects are equal if they are both Lists and have the same contents, even if they are different implementations of List. So coming back to the example in this question, it is possible to have a Map<ArrayList, Something> and for me to call remove() with a LinkedList as argument, and it should remove the key which is a list with the same contents. This would not be possible if remove() were generic and restricted its argument type.
Josh Bloch and Bill Pugh refer to this issue in Java Puzzlers IV: The
Phantom Reference Menace, Attack of the Clone, and Revenge of The
Shift.
Josh Bloch says (6:41) that they attempted to generify the get method
of Map, remove method and some other, but "it simply didn't work".
There are too many reasonable programs that could not be generified if
you only allow the generic type of the collection as parameter type.
The example given by him is an intersection of a List of Numbers and a
List of Longs.
Because if your type parameter is a wildcard, you can't use a generic remove method.
I seem to recall running into this question with Map's get(Object) method. The get method in this case isn't generic, though it should reasonably expect to be passed an object of the same type as the first type parameter. I realized that if you're passing around Maps with a wildcard as the first type parameter, then there's no way to get an element out of the Map with that method, if that argument was generic. Wildcard arguments can't really be satisfied, because the compiler can't guarantee that the type is correct. I speculate that the reason add is generic is that you're expected to guarantee that the type is correct before adding it to the collection. However, when removing an object, if the type is incorrect then it won't match anything anyway. If the argument were a wildcard the method would simply be unusable, even though you may have an object which you can GUARANTEE belongs to that collection, because you just got a reference to it in the previous line....
I probably didn't explain it very well, but it seems logical enough to me.
In addition to the other answers, there is another reason why the method should accept an Object, which is predicates. Consider the following sample:
class Person {
public String name;
// override equals()
}
class Employee extends Person {
public String company;
// override equals()
}
class Developer extends Employee {
public int yearsOfExperience;
// override equals()
}
class Test {
public static void main(String[] args) {
Collection<? extends Person> people = new ArrayList<Employee>();
// ...
// to remove the first employee with a specific name:
people.remove(new Person(someName1));
// to remove the first developer that matches some criteria:
people.remove(new Developer(someName2, someCompany, 10));
// to remove the first employee who is either
// a developer or an employee of someCompany:
people.remove(new Object() {
public boolean equals(Object employee) {
return employee instanceof Developer
|| ((Employee) employee).company.equals(someCompany);
}});
}
}
The point is that the object being passed to the remove method is responsible for defining the equals method. Building predicates becomes very simple this way.
Assume one has a collection of Cat, and some object references of types Animal, Cat, SiameseCat, and Dog. Asking the collection whether it contains the object referred to by the Cat or SiameseCat reference seems reasonable. Asking whether it contains the object referred to by the Animal reference may seem dodgy, but it's still perfectly reasonable. The object in question might, after all, be a Cat, and might appear in the collection.
Further, even if the object happens to be something other than a Cat, there's no problem saying whether it appears in the collection--simply answer "no, it doesn't". A "lookup-style" collection of some type should be able to meaningfully accept reference of any supertype and determine whether the object exists within the collection. If the passed-in object reference is of an unrelated type, there's no way the collection could possibly contain it, so the query is in some sense not meaningful (it will always answer "no"). Nonetheless, since there isn't any way to restrict parameters to being subtypes or supertypes, it's most practical to simply accept any type and answer "no" for any objects whose type is unrelated to that of the collection.
I always figured this was because remove() has no reason to care what type of object you give it. It's easy enough, regardless, to check if that object is one of the ones the Collection contains, since it can call equals() on anything. It's necessary to check type on add() to ensure that it only contains objects of that type.
It was a compromise. Both approaches have their advantage:
remove(Object o)
is more flexible. For example it allows to iterate through a list of numbers and remove them from a list of longs.
code that uses this flexibility can be more easily generified
remove(E e) brings more type safety to what most programs want to do by detecting subtle bugs at compile time, like mistakenly trying to remove an integer from a list of shorts.
Backwards compatibility was always a major goal when evolving the Java API, therefore remove(Object o) was chosen because it made generifying existing code easier. If backwards compatibility had NOT been an issue, I'm guessing the designers would have chosen remove(E e).
Remove is not a generic method so that existing code using a non-generic collection will still compile and still have the same behavior.
See http://www.ibm.com/developerworks/java/library/j-jtp01255.html for details.
Edit: A commenter asks why the add method is generic. [...removed my explanation...] Second commenter answered the question from firebird84 much better than me.
Another reason is because of interfaces. Here is an example to show it :
public interface A {}
public interface B {}
public class MyClass implements A, B {}
public static void main(String[] args) {
Collection<A> collection = new ArrayList<>();
MyClass item = new MyClass();
collection.add(item); // works fine
B b = item; // valid
collection.remove(b); /* It works because the remove method accepts an Object. If it was generic, this would not work */
}
Because it would break existing (pre-Java5) code. e.g.,
Set stringSet = new HashSet();
// do some stuff...
Object o = "foobar";
stringSet.remove(o);
Now you might say the above code is wrong, but suppose that o came from a heterogeneous set of objects (i.e., it contained strings, number, objects, etc.). You want to remove all the matches, which was legal because remove would just ignore the non-strings because they were non-equal. But if you make it remove(String o), that no longer works.

Categories