Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why UnaryFunction<Object> can be casted to UnaryFunction<T>?

When I read Effective Java item 27, the type casting between UnaryFunction<Object> and UnaryFunction<T> confused me.

interface UnaryFunction<T> {
    T apply(T t);
}

public class Main {
    private static final UnaryFunction<Object>  IDENTITY = new UnaryFunction<Object>() {
        public Object apply(Object t) {
            return t;
        }
    };

    @SuppressWarnings("unchecked")
    public static <T> UnaryFunction<T> identityFunction() {
        return  (UnaryFunction<T>) IDENTITY;
    }

    public static void main(String ... args) {
        UnaryFunction<A> identityA = Main.identityFunction();
        A a = identityA.apply(new A());
    }

}

class A {}

Why UnaryFunction<Object> can be casted to UnaryFunction<T>?

I know the generic type will be erased after complier. So (UnaryFunction<T>) IDENTITY will eventually be (UnaryFunction<Object>) IDENTITY, this will be working in Runtime.

But directly casting UnaryFunction<Object> to UnaryFunction<A> is not allowed by compiler.

UnaryFunction<A> identityA = (UnaryFunction<A>)IDENTITY;
//incompatible types: UnaryFunction<java.lang.Object> cannot be converted to UnaryFunction<A>

And there is no inheritance relationship between UnaryFunction<Object> and UnaryFunction<T>. So why UnaryFunction<Object> can be casted to UnaryFunction<T>?

like image 825
Robin Han Avatar asked Sep 10 '16 06:09

Robin Han


1 Answers

All generic types, without a bound (... extends ..., is considered Object due to type erasure. So this cast might be valid for some values of T. @SuppressWarnings("unchecked") tells the compiler you are doing the right thing so the warning is suppressed.

Casting to a specific type is problematic. Let's assume you are dealing with List<Object> which you cast to List<A> where A is a class. In this case list might have elements which are a supertype of A so this cast is unsound hence the compiler does not allow it. In case of the function (UnaryFunction<Object>) it is implied that this can return an Object. Say your code was IDENTITY = t -> new Object(); in which case casting to UnaryFunction<A> would be unsound as it returns an Object. In the case it is UnaryFunction<T> there is some type T which satisfies the cast, that is, when T is an Object.

For some background reading on this see: Liskov substitution principle which deals with subtyping of functions.

like image 102
Suminda Sirinath S. Dharmasena Avatar answered Sep 25 '22 16:09

Suminda Sirinath S. Dharmasena