Search code examples
javagenericstype-safetyraw-types

How To Ensure the Validity of Parameterized Types Prior to Object Instantiation


While studying Item 23 of Effective Java 2nd Edition I decided to attempt instantiating an object with a constructor that has a raw collection with an argument list of mixed objects. I assumed the compiler would pick up that I was attempting to pass a raw type to the constructor. However, the compiler only provides an unchecked exception warning, "at the caller, not the callee."

The class compiles fine, further supporting the statements by Josh Bloch not to use raw types in new code. The fact this compiles is a bit disturbing to me.

Q? How can one ensure the type safety of a class prior to instantiating the object short of having to manually check for object validity in the constructor? The following only provided an unchecked assignment warning from "the caller... in main" How could one defensively program against this.

public class RawType {

    private final Collection<Example> example;

    public RawType( Collection<Example> example ) {
        this.example = example;
    }

    public Collection<Example> getExample() {
        return example;
    }

    public static void main( String[] args ) {

        Collection unsafe = new ArrayList();

        unsafe.add( new Example() );
        unsafe.add( new Corruption() );
        unsafe.add( new Example() );

        RawType rawType = new RawType( unsafe ); // Corruption

        for(Object type : rawType.getExample()) {
            System.out.println(type.getClass().getName()); // Mixed Classes...
        }

    }

    static class Corruption {}

    static class Example{}

}

Solution

  • Below lines compile but generate warnings as well.

    Collection unsafe = new ArrayList(); // RAW type
    unsafe.add(1);                       // no compile time error (just warning)
    
    Collection<String> example = unsafe; // assign RAW type to parametrized type
    example.add(1);                      // compile time error
    

    Better use Generic collection to avoid such situation then add the values in it. Never mix RAW and parametrized type.


    Have a look at below code:

    Collection<Integer> unsafe = new ArrayList<Integer>();
    unsafe.add(1);
    Collection<String> example = unsafe; // compile time error
    

    Read more...


    If you can't avoid RAW type then use below code in the constructor:

    Collection unsafe = new ArrayList(); 
    unsafe.add(1);                       
    unsafe.add("hello");
    
    // in the constructor, just add String values from the unsafe collection
    Collection<String> example = new ArrayList<String>(); 
    for(Object value:unsafe){
        if(value instanceof String){
            example.add((String)value);
        }
    }