Home > Software design >  Why does the generic type of an Optional<Long> field get erased when I declare the containing
Why does the generic type of an Optional<Long> field get erased when I declare the containing

Time:03-10

I have a class called CacheRequestWrapper that has one field with a generic type. It also has another field of type Optional<Long>. When the class is made generic the value of this Optional field will no longer be of type Long. Instead it will become of type Object.

In other words, when I make the class generic the generic type of Optional is erased and i don't understand why. Only the field cacheReturnValueshould be of type T but it seems like the type T is also assigned to Optional. Let me explain why this seems to be the case with the example below:

public class CacheRequestWrapper<T> extends CacheWrapper {

    private T cacheReturnValue;

    private Optional<Long> requestingUserId;

    public Optional<Long> getRequestingUserId() {
       return requestingUserId;
    }

    public void setRequestingUserId(Optional<Long>requestingUserId) {
       this.requestingUserId = requestingUserId;
    }
    
    //other Getters, setters
    
}


class Service{
    //1.
    private void myRawTypedMethod(CacheRequestWrapper cacheRequestWrapper) {

        Long viewerId = cacheRequestWrapper.getRequestingUserId().get(); //This gives the compile error 'required type: Long, provided type Object'

    }
    //2.
    private <T> void myGenericTypedMethod(CacheRequestWrapper<T> cacheRequestWrapper) {

        Long viewerId = cacheRequestWrapper.getRequestingUserId().get(); //No compile error here.

    }
}

In method 1. I get a compile error when I'm assigning the optional value to viewerId. In generic method 2. the compile error is not present. Its unclear why but what i think is happening is that the Long type of Optional is replaced by T for some reason. This causes a problem because during runtime T is not always of the same type as Long.

So why is the generic type of the Optional field overridden by the generic type T?

CodePudding user response:

This behavior is specified in Section 4.8 of the Java Language Specification. A generic type without the type parameters is called a raw type, and in a raw type almost all generics are erased.

Raw types are only intended to interact with legacy Java code (version 1.4 or lower) which doesn't have generics, and to facilitate this interaction all generics need to be erased because the legacy code might want to do things which the generics would forbid.

For example, something like

String id = (String)wrapper.getRequestingUserId().orElseThrow();

would not compile if the generics were not erased. I admit, this is a strange example because Optional was added to the Java language later than generics and would of not appear in legacy code, but Java does not make an exception for it.

Since pre-1.4 java code is becoming increasingly rare, new code should rarely use raw types.

  • Related