Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Different return types of abstract method in java without casting

I am trying to implement and override a method with different return types without being forced to cast the return type.

public abstract class A {
public abstract Object getValue(String content);
}

public class B extends A {
public String getValue(String content) {...}
}

public class C extends A {
public int getValue(String content) {...}
}


public class D extends A {
public boolean getValue(String content) {...}
}

// Main loop:
for (A a : allAs)
{
// I want to use the method getValue() and corresponding to the type return a String, int or boolean without casting the return type
}

My question: Is it possible to return different types without being forced to cast? How has the abstract method look like to solve the problem?

I think there has to be a solution because the compiler should know the return type...

like image 528
erwingun2010 Avatar asked Feb 23 '13 06:02

erwingun2010


4 Answers

In your example, classes C and D will not compile. The overridden methods in them violate the Liskov substitution principle, aka, their return type is incompatible with their parent class. What you are looking to do can be accomplished with generics, as long as you are willing to forego the use of primitives as your return type.

abstract class A<T> {
    public abstract T getValue(String content);
}

class B extends A<String> {
    public String getValue(String content) { }
}

class C extends A<Integer> {
    public Integer getValue(String content) { }
}

class D extends A<Boolean> {
    public Boolean getValue(String content) { }
}
like image 116
Perception Avatar answered Oct 11 '22 02:10

Perception


What you describe is not possible in general. However, if the subclass returns a "narrower" subtype of the superclass method return, this is called a "covariant return type" and is allowed in Java since JDK 1.5. However, based on your example I do not think covariant return is what you are looking for.

I assume what you want is

for (A a : allAs)
{
    String b = a.getValue();
    int    c = a.getValue();
}

The problem here is, of course, that the compiler has no way of knowing at compile time which of those two statements is correct, and they can't both be correct.

like image 38
Jim Garrison Avatar answered Oct 11 '22 03:10

Jim Garrison


You could use generics.

public abstract class A<T> {
   public abstract T getValue(String content);
}

public class B extends A<String> {
   public String getValue(String content) {...}
}

etc... int doesn't work as a return type for this, but Integer would.

I'm not typing at a compiler so there may be typos...

As noted by Jim and Chris, if you are looping over As, you can only get the "A" result, which is Object.

like image 2
user949300 Avatar answered Oct 11 '22 01:10

user949300


In your example, the definition of class B is ok, since String is a subclass of Object. The other two wont compile, since they are primitive types. You could replace them with Integer and Boolean returns to resolve that though.

As for your main loop, if you're iterating over them as references to A, you'll only be able to use A's definition of the method, which returns Object.

like image 1
Krease Avatar answered Oct 11 '22 01:10

Krease