2

I've an interface Message with two implementations MessageA and MessageB.

Now I have the following generic class that takes a type variable T that extends Message. I want a method test to act in one way if the argument is T (or a subtype) and in an other way if the argument is an other implementation of Message :

I tried this, but it does not work because of erasure :

public class MyClass<T extends Message>               
{                                                     
    public Integer test(T m)                          
    {                                                 
        System.out.println( "This is the good one." );
        return m.f(7);                                
    }                                                 
    public <S extends Message> Integer test(S m)                        
    {                                                 
        System.out.println("I'm not the good one");   
        return m.f(7);                                
    }                                                 
}                                                     

I could do explicit type checking, but I guess that there exists a cleaner way to reach my goal.

EDIT

I prefer to use overriding or overloading because in my project, I will have a class MyClassA implements MyClass<MessageA> that will implement the method test(MessageA m). But I want to implement the "wrong" case in the base generic class.

The following makes the work :

public class MyClassA extends MyClass<MessageA> 
{                                               
    public Integer test(Message m)              
    {                                           
        if (m instanceof MessageA)              
        {                                       
            return m.f(7);                      
        }                                       
        else                                    
        {                                       
            System.out.println("Bad type.");    
            return 0;                           
        }                                       
    }                                           
}                                               

But it forces me to implement a if-then block with print("bad type") in every instantiation of my generic MyClass<T extends Message> (for the sake of code duplication).

EDIT Based on the accepted solution, I posted a fully-functional minimal example here.

Laurent Claessens
  • 547
  • 1
  • 3
  • 18
  • Instead of type checking.. you can check for class names. `obj.getClass().getName().equals(Message.class.getName())` – Yogesh Patil Jan 25 '16 at 10:15
  • You want to understand what object orientation; especially regarding polymorphism is really about: you do **not** want to call a specific method to distinct one subclass from another subclass. Instead, you want to call the **same** method on both classes; and then two different implementations to do different things. – GhostCat Jan 25 '16 at 10:20

3 Answers3

0

Overloading is static. It uses the compile-time object type instead of the actual run-time type. Either use Message#test() to use overriding or add explicit type checking (i.e. instanceof) to your code.

dzidzitop
  • 385
  • 2
  • 11
0

You can test using the instanceof keyword :

public Integer test(T m)                          
{
   if(m instanceof MessageA || m instanceof MessageB)  {                                            
        System.out.println( "A subclass of Message instance." );
        //your custom process
   }else{
        System.out.println( "A Message instance." );
        //your custom process
   }
}  

EDIT

Due to the way generics is implemented in Java, you can't find out what type of Object a generic class is using at runtime (the generic type T is not kept at runtime). Still, you can use a private data member and overload your test method in every Message branch and use it in the generic Class :

public class MyClass<T extends Message> 
{
    private Class<T> type;

    public MyClass(Class<T> type) { this.type = type; } 

    public Integer test(T m)                          
    {                                                 
       return type.test(m);                               
    }   

}
Wael Sakhri
  • 397
  • 1
  • 8
  • Because of the generics, it seem to be difficult : `if (m instanceof T)` results in a `unexpected type` error. **EDIT : ParkedHalo** said it. – Laurent Claessens Jan 25 '16 at 10:32
  • Even if it works (m instanceof T) theoretically it must always return true. because any mS instance should be a T or subclass instance. – Wael Sakhri Jan 25 '16 at 10:41
  • Sakhi. No because T is a subtype of Message. There can be other subtypes. In other words, I want to test if `m` lies in the `T`-branch of the Message's hierarchy. – Laurent Claessens Jan 25 '16 at 10:54
  • What do you mean in your question with : "T (or a subtype)" and "another implementation of Message" (Waht's différence) ? – Wael Sakhri Jan 25 '16 at 11:15
  • There is [Class.isInstance](https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html#isInstance-java.lang.Object-) – lschuetze Jan 25 '16 at 11:30
  • I mean that Message can have two subtypes S and T (extending Message in two different ways). Both S and T can have a hierarchy. – Laurent Claessens Jan 25 '16 at 12:18
0

My answer may sound harsh, but your design seems a little broken.

The easiest solution is to derive MessageA and MessageB from a common class (which may have nothing to do with your interface), and to add a protected function to this class which you can call.

Or you create a second interface, and follow one of the questions below.

See one of the SO questions below:

Generic extending class AND implements interface

Java generics - Make Generic to extends 2 interfaces

Community
  • 1
  • 1
Alexander Kemp
  • 202
  • 1
  • 10