Java original class method call from polymorph list - java

I have an abstract superclass
public abstact class SuperClass
2 classes extend this superclass
public class ChildClass1 extends SuperClass
public class ChildClass2 extends SuperClass
I have an arraylist
List<SuperClass> objects = new ArrayList<SuperClass>();
I have a method that has 2 different parameter list
public void get(ChildClass1 o)
public void get(ChildClass2 o)
When trying something like this:
for (SuperClass o : objects)
get(o);
I got an error: "The method get() is not applicable for the arguments SuperClass"
How should I implement this in a way that every element in the list could call the right method? Is the only way I can implement this is with instanceof and casting it or is there a nicer way?

This is not possible as Java decides at compile time which one of these methods to use. You could have a get method defined in SuperClass, then the code would look like this:
for (SuperClass o : objects)
o.get();
A quick Stackoverflow search lead me to this thread there is more information for you: overloading and compile time binding. Someone proposed to use the visitor pattern there. That might be an alternative, too. As every Java developer encounters the issue you have asked sooner or later I am sure there are plenty of interesting threads on this topic to be found here on Stackoverflow.

Java binds (most) method invocations dynamically with respect to the object on which they are invoked, but it determines type signatures statically. Thus, when your code ...
for (SuperClass o : objects)
get(o);
... is analyzed at compile time, Java must select a get() method signature based only on the information it has at that time, which is that o is a SuperClass. Moreover, it will choose exactly one method signature, for an existing method on the relevant class. At run time, it will resolve that method signature against the class of the object on which it is invoked, considering other signatures only to the extent needed for supporting covariant return types.
That you have a requirement such as you do creates a distinct code smell. It makes sense to collect objects in a List<SuperClass> only for purposes that are served equally well (and perhaps polymorphically) by any SuperClass, which is not the case for you. If you cannot change that aspect of the design, then there are multiple ways you could proceed, including ...
An ugly hack way
Use instanceof and casting:
for (SuperClass o : objects) {
if (o instanceof ChildClass1) {
get((ChildClass1) o);
} else if (o instanceof ChildClass2) {
get((ChildClass2) o);
} else if (o == null) {
// do something appropriate
} else {
throw new SomeKindOfException(
"Don't know what to do with a " + o.getClass());
}
}
Double dispactch / inversion of control
Give the superclass a method ...
abstract void acceptGetter(ClassThatGets objectThatGets);
... and have each subclass implement it the same way:
void acceptGetter(ClassThatGets objectThatGets) {
objectThatGets.get(this);
}
Then replace your problematic loop with ...
for (SuperClass o : objects)
o.acceptGetter(this);
... where it is assumed that that code appears in class ClassThatGets, which declares the get() methods you described, or in a subclass of that class.
There are surely other alternatives as well, some of which may fit more cleanly into your overall design.

Related

Compiler Prefers Object's Method to Implementation

I came over a strange behavior of the Eclipse compiler today and I'm not sure what to think of it. We're trying to create a useful Cloneable interface like that:
public interface PublicCloneable extends Cloneable {
Object clone();
static <T extends PublicCloneable> T clone(final T obj) {
if (obj != null) {
return (T) obj.clone();
}
return null;
}
}
The fun part is that the compiler complains about obj.clone(): Unhandled exception type CloneNotSupportedException
I know how to fix it, we can just cast obj to PublicCloneable and be done with it. But what I'm interested in: why would the compiler prefer the method of Object to a method of an implementation?
From this answer: https://stackoverflow.com/a/13776045/896588
[...] while the interface itself doesn't extend Object, it is known that any implementation will.
So since you have an interface, the declared clone() doesn't override the clone() of Object. It is formally a newly declared operation at this point.
Try implementing your interface. You will see that it forces you to override clone() from Object but without the exception since it also has to fulfill the interface.
You have created a tricky and complex situation here. The compiler knows that your implementing class will also extend Object and inherit clone() with the exception. When it looks for a declaration in the type hierachy, it finds that one first and stops searching. It doesn't care anymore that PublicCloneable (which will have a clone() without exception) is in the type hierachy too. If you change the parameter type directly to PublicCloneable, that's where it start searching and finds the other one.
This might be explained too simplified but I can't seem to find the corresponding formal parts in the JLS.

Dynamically cast to subclass to use correct method overload

I'm trying to make my code more polymorphic. Right now I have a superclass that has four different subclasses. I have a method that accepts the superclass as a type. I want to perform some generic actions before routing it to a different method to handle other actions. Here's what I am envisioning:
public void performSomething(Super object) {
//do some generic action each time to object
object.setSuperProperty();
//now route to appropriate method to perform specific action
doSpecific(object);
}
private void doSpecific(SubA object) { }
private void doSpecific(SubB object) { }
private void doSpecific(SubC object) { }
private void doSpecific(SubD object) { }
This way if I want to add more functionality -- by creating a new subclass or whatever -- then I just need to add another method with the correct subclass type. However, this is not possible since the compiler complains about not having a doSpecific(Super object) method. Instead, in performSomething(Super object) I have to do an ugly:
if(object instanceof SubA)
doSpecific((SubA)object);
else if(object instanceof SubB)
doSpecific((SubB)object);
...
Is there a better way to do this than having to perform all the instanceof checks? Is there a design pattern that I'm not thinking of? I know that I'll lose the compile-time type check safety, but just curious what other solutions could possibly exist.
edit: Forgot to mention this. performSomething and doSpecific are part of an unrelated class I'll call ClassA. I considered creating an abstract method in the Super class so that the subclass could properly implement it. The problem is that performSomething and doSpecific depend on roughly 8 different members of ClassA. So if I wanted to delegate the method to the subclass it would require a ton of parameters like subB.doSpecific(int, int, String, String, Object, int, long, blah, blah); which I'm not sure is better than the original instanceOf check. This would also create a tight coupling between ClassA and the Super/Sub classes I have, when doesn't seem right since I just need to read values from them.
I recommend the Command Pattern.
That means: Every of your subclasses implements a doSpecific() method. Then your initial method looks like this:
public void performSomething(Super object) {
//do some generic action each time to object
object.setSuperProperty();
//now route to appropriate method to perform specific action
object.doSpecific(...);
}
The compiler picks the method of the subclass automatically - no instanceOf check for you.

Can I work with generic types from a calling class?

I'm currently brushing up my Java and reading up on Generics. Since they were not treated extensively in my Java class, I'm still having some trouble wrapping my mind about it, so please keep that in mind when answering.
First of all, I'm pretty sure that what I'm trying to is not possible. However, I'd like to find out where my thinking is wrong and how I should go about achieving what I want.
What I'm trying to do is manipulating an object that implements a generic interface from another class that has no knowledge about the instantiated type. Thus, I have something like the following classes:
public interface CalledInterface<E> {
public E get() { ... }
public set(E e) { ... }
}
public class Called implements CalledInterface<String> {
...
}
Now what I want to do is:
public class Caller {
protected CalledInterface<?> c;
public Caller (CalledInterface<?> arg) {
c = arg;
}
public void run(){
// I can do this:
c.set(c.get());
// But I'd want to be able to do something like:
<?> element = c.get();
c.set(element);
}
}
What is the fundamental flaw in my thinking, if there is one? And what approach should I rather be taking?
First of all, keep in mind that generics is a compile time thing not a runtime.
Now in your Caller you defined Called c. Called is defined to implement CalledInterface<String>, so automatically, Called has the following methods generated at compile time:
String get();
void set(String e); //i assume you wanted to return void
So essentially this doesn't really make sense:
<?> element = c.get();
The Caller class isn't even aware Called is using generics internally, for it, Called just deals with strings.
UPDATE
Based on your comment, since you don't want Caller to use Called directly but use CalledInterface first thing you have to do is change the type of c to that. In this case you should not use generics, because the whole point of generics is that the same class is used in different scenarios with different types (again determined at compile time), enforcing types without having repeated code.
If I understand correctly you don't want to restrict Caller to use String, so what you have to do is change CalledInterface to not use generics, and change the methods to:
Object get();
void set(Object o);
This is how we used to do things before Generics in Java 1.4. You obviously run the risk of not having type safety, so think through whether what you want really makes design sense, because it probably does not because you have to do instanceof anyway to check the type to use the Object in a useful way (i.e. to access its methods).
If on the other hand you just change the c member (and the constructor argument of Caller) to:
CalledInterface<String> c;
Your Caller will be interacting with the CalledInterface rather than the implementation and at the same time still be type safe. So you can still pass an instance of Called and set it to c.
After your edit:
// I can do this:
c.set(c.get());
No you can't. It won't compile with c being CalledInterface<?>. (Have you even tried it?)
To do this, you can use a "capture helper":
private static <T> void helper(CalledInterface<T> c) {
c.set(c.get());
}
public void run(){
helper(c);
}
Which also solves your second problem:
private static <T> void helper(CalledInterface<T> c) {
T element = c.get();
c.set(element);
}
public void run(){
helper(c);
}
There are a few minor mistakes in your code:
protected Called c;
public Caller (CalledInterface arg) {
c = arg;
}
You are not allowed to assign arg here, because the type CalledInterface is not a subtype of Called (it is the other way around)
Also you should give type information when using CalledInterface (it is allowed to leave it out, but only for legacy purposes).
Now to the part you are wondering about. For the type Called, the compiler knows get() returns a String, if you are not interested in that, you can of course always use Object as the type of element. The compiler also knows that set() takes a String as argument, so it requires you to give one. In generics is essentially the same as using Object in a case without generics (even though it isn't allowed on the location you use it, because it doesn't make sense). This means that you would be telling the compiler to forget the type on the first line (calling get()) and to unforget it on the line below.

Checking generic type

The Java Collections interfaces (for example, List or Set) define the contains method to accept any Object.
public boolean contains(Object o)
However, when it comes to implementing this method, the particular collection I'm working on requires that I have a type which is compatible with the generic type E of the class (i.e. either be class E, or a subclass of E, or a class which implements E if E is an interface). In other words, if o is castable to type E, then it is compatible. This presents an issue because Java erases the Generic type information, so something like this isn't possible:
public boolean contains(Object o)
{
if(o instanceof E) // compile error due to type erasures
{
// ... check if this collection contains o
}
return false;
}
My question is what would be the best way to accomplish something like this? The Oracle Article on Type Erasures mentions that this forbidden, but does not offer any solutions to this problem.
I can only think of one semi-elegant way to get around this:
Make a cast to type E. If the cast fails, o cannot be type E or a subclass of type E.
public boolean contains(Object o)
{
try
{
E key = (E) o; // I know it's unsafe, but if o is castable to E then the method should work
// check if this collection contains key
}
catch(ClassCastException e)
{
// invalid type, cannot contain o
}
return false;
}
While this would work, it looks messy (I'm not a big fan of using Exceptions in this manner).
Is there a better way to accomplish this same goal (changing the method signature is not allowed)?
edit: yeah, this doesn't work because E gets erased to Object :(
This is only possible by (1) passing in the expected Class, or (2) examining the generic type parameters of some reflective element that defines <E>. Let me elaborate.
The most common case is to simply require the caller to pass in the runtime class of E.
public class MyClass<E> {
private final Class<E> realType;
public MyClass(Class<E> realType) {
this.realType = realType;
}
public boolean Contains(Object o) {
E e = realType.cast(o); // runtime cast - will throw ClassCastException.
// Could also use realType.isInstance(o)
// or realType.isAssignableFrom(o.getClass())
...
}
}
Caller:
new MyClass<MyObject>(MyObject.class)
This is generally type safe since the compiler will verify that the <E>'s match. Of course the caller can bypass the compiler's checks...nothing you can do about that!
For (2), what I mean is that you can use reflection to examine static generic type parameters. This probably isn't a good option in your case because you must have access to some field, method, or superclass declaration that statically defines <E>. The most common way is to make your class abstract and have callers extend it. This approach is used by Hamcrest's TypeSafeMatcher (see ReflectiveTypeFinder) to great effect. (Note that TypeSafeMatcher is basically just making option (1) easier for the programmer; it still provides a constructor that takes the Class for cases when reflection doesn't work!) If you want to get really fancy, you can inspect getClass().getGenericSuperclass().getActualTypeArguments(). This isn't as easy as it sounds -- see this good article. I'm not even sure that article covers all the edge cases -- you're basically reimplementing the compiler! So just go with option (1) and be happy you're not using generics in C# :-)
I don't get why you are messing with .contains() in this way. The specification of contains() in Collection says that it returns true if the given object equals (in the sense of .equals()) some element of the container. So are you changing the meaning of .contains()? How is your equality defined?

Interface implementation with method argument superclasses

As a practical example of the general question in the subject, I'd like to implement the containsAll method in the Set interface with
public boolean containsAll(Iterable<?> c) { /* ... */ }
I figure this should be allowed, since Collection is Iterable meaning such a containsAll would cover the interface requirement. Likewise, more generally being able to implement interfaces with argument superclasses seems like it should work.
However, Eclipse says no way (haven't tried just javac straight-up) - can someone explain the reason for that? I'm sure there's something in the spec which makes it the way it is, but I'd like to understand the motivation for requirement as well. Or am I missing something like Iterable<?> not being a superclass of Collection<?>?
As a side question - given I'm declaring two methods would the method with the Iterable signature always be preferred on calls with a Collection argument?
Eclipse Error:
If I remove the method with the Collection signature, just leaving the Iterable one (see after error), I get the following:
The type BitPowerSet must implement the inherited abstract method Set<Long>.containsAll(Collection<?>)
The exact implementation being:
#Override public boolean containsAll(Collection<?> c) {
for (Object o : c) if (!contains(o)) return false;
return true;
}
public boolean containsAll(Iterable<?> c) {
for (Object o : c) if (!contains(o)) return false;
return true;
}
Since the interface you are implementing declares the (abstract) method containsAll(Collection<?>), you must implement it with this exact signature. Java does not allow you to implement/override a method with a wider parameter type than the original. This is why you get the error you show when you comment out your method with the Collection signature.
You don't show the other error you claim to get when the method is not commented out, but I guess it might have to do something with ambiguous method overloading.
My guess as to why java has this restriction is, say you have:
class A {
void foo(String s) { ... }
}
class B extends A {
// Note generalized type
#Override void foo(Object s) { ... }
}
Now if you have class C extends B and it wants to override foo, it's not clear what argument it should take.
Say for example C extended A directly at first, overriding void foo(String s), and then it was changed to extend B. In this case C's existing override of foo would become invalid because B's foo should be able to handle all Objects, not just Strings.
The argument types are part of the method signature so the jvm needs a method with exact the same signature to find overrides. A containsAll( Iterable) will have a different signature than containsAll(Collection).
If I remember right the compiler has to use some workarounds to make generics work in spite of this limitation.
To your second question, the compiler would prefer the Collection argument since it is a subtype of Iterable, this makes the Collection method more specific than the Iterable one.

Categories

Resources