If I have a class hierarchy like this
AbstractSuperClass
ConcreteClassA
ConcreteClassB
is it possible to have a static method in AbstractSuperClass
which returns the class - not an instance - of one of the two concrete classes?
I've tried returning Class<AbstractSuperClass>
, but the IDE (Android Studio) says
Incompatible types.
Required: Class <com.example.AbstractSuperClass>
Found: Class <com.example.ConcreteClassA>
Here's an example of what I'm thinking, but which isn't working:
public abstract class AbstractSuperClass{
public abstract void someAbstractMethod();
public static String getSomeText(){
return "This is AbstractSuperClass";
};
public static Class<AbstractSuperClass> getConcreteClass(int x){
switch( x ){
case 0: return ConcreteClassA.class;
case 1: return ConcreteClassB.class;
}
}
}
public class ConcreteClassA extends AbstractSuperClass{
public abstract void someAbstractMethod(){
// Do something
}
public static String getSomeText(){
return "This is ConcreteClassA";
};
}
public class ConcreteClassB extends AbstractSuperClass{
public abstract void someAbstractMethod(){
// Do something
}
public static String getSomeText(){
return "This is ConcreteClassB";
};
}
AbstractSuperClass.getConcreteClass(1).getSomeText(); // Should return "This is ConcreteClassB"
Is this simply impossible in Java, or am I just doing it wrong?
I'm not sure what it is needed for, but there are several issues in your code.
- You can not return Class because you are returning a class that extends the AbstractSuperClass. So you have to inform the compiler about it and change the return type to Class
You can not call the any (doesn't matter static or not static method) directly from class. You need to use the reflections API, so you method call will look like this:
final Class clazz =
AbstractSuperClass.getConcreteClass(1);
Object result = clazz.getMethod("getSomeText").invoke(clazz);
So that's the complete code of you class.
package com.mycompany.sandbox;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;
abstract class AbstractSuperClass{
public abstract void someAbstractMethod();
public static String getSomeText(){
return "This is AbstractSuperClass";
};
public static Class<? extends AbstractSuperClass> getConcreteClass(int x){
switch( x ){
case 0: return ConcreteClassA.class;
case 1: return ConcreteClassB.class;
}
return null;
}
}
class ConcreteClassA extends AbstractSuperClass{
public void someAbstractMethod(){
// Do something
}
public static String getSomeText(){
return "This is ConcreteClassA";
};
}
class ConcreteClassB extends AbstractSuperClass{
public void someAbstractMethod(){
// Do something
}
public static String getSomeText(){
return "This is ConcreteClassB";
};
}
// one class needs to have a main() method
public class HelloWorld
{
// arguments are passed using the text field below this editor
public static void main(String args[])
{
try {
final Class<? extends AbstractSuperClass> clazz = AbstractSuperClass.getConcreteClass(1);
Object result = clazz.getMethod("getSomeText").invoke(clazz);
System.out.println(Objects.toString(result, "<NULL>"));
} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
Logger.getLogger(HelloWorld.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
You can't invoke a static
method directly from a Class
like you are trying to do, you will need reflection to invoke your getSomeText()
method. Like,
try {
Method m = AbstractSuperClass.getConcreteClass(1).getMethod("getSomeText",
new Class[0]);
System.out.println(m.invoke(null, new Object[0]));
} catch (Exception e) {
e.printStackTrace();
}
Then you can fix your getConcreteClass
method by using capture-of extends AbstractSuperClass
(producer extends, consumer super). And you must have a default return for the case where nothing matches. Like,
public static Class<? extends AbstractSuperClass> getConcreteClass(int x) {
switch (x) {
case 0: return ConcreteClassA.class;
case 1: return ConcreteClassB.class;
}
return null;
}
Which I ran (and got)
This is ConcreteClassB
For this method,
public Class<AbstractSuperClass> getConcreteClass(int x){
switch( x ){
case 0: return ConcreteClassA.class;
case 1: return ConcreteClassB.class;
}
}
the signature should be
public Class<? extends AbstractSuperClass> getConcreteClass(int x)
which means that the return value can be AbstractSuperClass.class
or any subtype of it.
Try this:
public abstract class AbstractSuperClass {
public abstract void someAbstractMethod();
public static String getSomeText() {
return "This is AbstractSuperClass";
};
public Class<? extends AbstractSuperClass> getConcreteClass() {
return this.getClass();
}
}
In fact you don't need the int
parameter as the instance of the subclass will know its type without the need of a hint.