How to resolve type on runtime in generics overloading java

1k Views Asked by At

I am tying to resolve type on runtime with generics. Here is my code.

public class NewClas {
    public static void main(String[] args) {
        Object object = null;
        String string = null;
        Integer integer = null;
        // with out generics
        print(object);
        print(string);
        print(integer);
        // print(null); // this won't complile

        // with generics
        Verify<Object> objectVerify = new Verify<Object>();
        Verify<String> stringVerify = new Verify<String>();
        Verify<Integer> integerVerify = new Verify<Integer>();

        objectVerify.call();
        stringVerify.call();
        integerVerify.call();

    }

    public static void print(Object obj) {
        System.out.println("Obj");
    }

    public static void print(String string) {
        System.out.println("string");
    }

    public static void print(Integer integer) {
        System.out.println("Int");
    }
}

// if T extends String then only print(String string) will call
class Verify<T> {
public T obj = null;

    public void call() {
        print(obj);
    }

    // even this is not working
    /*public <T> void call() {
        print((T)obj);
    }*/

    public  void print(Object obj) {
        System.out.println("Obj");
    }

    public  void print(String string) {
        System.out.println("string");
    }

    public  void print(Integer integer) {
        System.out.println("Int");
    }
}

How do I utilize/call methods print(String string),print(Integer integer) in generic class.

Even I have tried

stringVerify.obj =  new String();
stringVerify.call();

which is calling same method with object signature.

Also tried following, which is also invoking same method with object signature.

Verify<String> stringVerify = new Verify<String>() {};
stringVerify.obj =  new String();
stringVerify.call();

for all above scenarios output will be.

Obj
string
Int
Obj
Obj
Obj

Following is what I want

Obj
string
Int
Obj
string
Int

please educate me.

4

There are 4 best solutions below

0
On

Generics type erasure. In your case generic type <T> erased to Object.

For more information read this documentation

https://docs.oracle.com/javase/tutorial/java/generics/erasure.html

Sorry for my bad English.

1
On

Check for instanceof in your call method of Verify class:

public void call() {
 if(obj.instanceof(String)) {
    print((String)obj);
 } else if (obj.instanceof(Integer) {
    print((Integer)obj)
 }
2
On

Your code works as it should. The first three results are correct because method overloading is resolved at compilation. This means that the method whose signature argument matches the type of the parameter used when calling the method will be used.

Regarding the calls made from the Verify class, you must take into account that type information is erased at compilation. This means that for all the instances of your class, the obj variable's type will be Object. Because of this, the same result (Object) will be printed for all calls.

If you use <T extends String> as the type declaration for your Verify class, at compilation the obj reference variable will be of type String.

You can modify your class like this, but this won't allow you to pass a null object since it will throw a NPE:

 public Verify(T obj) {
    this.obj = obj;
    this.type = (Class<T>) this.obj.getClass();
 }

 public void call() {
    if (type == Object.class) {
        print(obj);
    } else if (type == String.class) {
        print((String) obj);
    } else if (type == Integer.class) {
        print((Integer) obj);
    }
}

You can also send the class as an explicit parameter and no NPE will be thrown:

    private Class<T> type = null;

    public Verify(Class<T> type, T obj) {
       this.obj = obj;
       this.type = type;
    }
0
On

Try with this implementation :

class Verify<T> {
        public final T obj;

        Verify(T obj) {
            this.obj = obj;
        }


        public void print() {

            switch (obj.getClass().getSimpleName()) {
                case  "String" :
                    System.out.print("string");
                    break;
                case "Integer" :
                    System.out.print("Int");
                    break;
                 default:
                     System.out.print("Object");
            }
        }

    }