简体   繁体   中英

Different Java behavior for Generics and Arrays

Why does java allows inconsistent type to be entered into a generic object reference but not in an array?

For Eg:

When initializing array:

int[] a = {1, 2, 3};

And, if I enter:

int[] a = {1, 2, "3"}; //Error for incompatible types

While for generics,

import java.util.ArrayList;

public class Test {
    private static ArrayList tricky(ArrayList list) {
        list.add(12345);
        return list;
    }
    public static void main(String[] args) {
        int i = 0;
        ArrayList<String> list = new ArrayList<>();
        list.add("String is King");
        Test.tricky(list);
    }
}

The above code will let you add any Type in the list object, resulting in a run time exception in some cases.

Why is there such a behavior?? Kindly give a proper explanation.

The method's parameter has no generic so all classes are allowed. You may google 'type erasure' for more information.

If you add the generic type to your method you will get a compiler error:

private static ArrayList<String> tricky(ArrayList<String> list) { // ...

By the way, you do not need to return the list because you modify the same instance.

Here's why:

The reason you can get away with compiling this for arrays is because there is a runtime exception (ArrayStoreException) that will prevent you from putting the wrong type of object into an array. If you send a Dog array into the method that takes an Animal array, and you add only Dogs (including Dog subtypes, of course) into the array now referenced by Animal, no problem. But if you DO try to add a Cat to the object that is actually a Dog array, you'll get the exception. Generic Methods (Exam Objectives 6.3 and 6.4) 615 616 Chapter 7: Generics and Collections

But there IS no equivalent exception for generics, because of type erasure! In other words, at runtime the JVM KNOWS the type of arrays, but does NOT know the type of a collection. All the generic type information is removed during compilation, so by the time it gets to the JVM, there is simply no way to recognize the disaster of putting a Cat into an ArrayList and vice versa (and it becomes exactly like the problems you have when you use legacy, non-type safe code)

Courtesy : SCJP Study guide by Kathy Sierra and Bert Bates

When you declare you ArrayList like ArrayList list = ... you do not declare the type of object your list will contain. By default, since every type has Object as superclass, it is an ArrayList<Object> . For good practices, you should declare the type of your ArrayList<SomeType> and, thereby, avoid adding inconsistant elements (according to the type)

Because you haven't defined the generic type of your list it defaults to List<Object> which accepts anything that extends Object .

Thanks to auto-boxing a primitive int is converted to an Integer , which extends Object , when it is added to your list.

Your array only allows int 's, so String 's are not allowed.

This is because in your method parameter you did not specify a particular type for ArrayList so by default it can accept all type of objects.

import java.util.ArrayList;

public class Test {

//Specify which type of objects you want to store in Arraylist
    private static ArrayList tricky(ArrayList<String> list) {
        list.add(12345);   //This will give compile time error now
        return list;
    }
    public static void main(String[] args) {
        int i = 0;
        ArrayList<String> list = new ArrayList();
        list.add("String is King");
        Test.tricky(list);
    }
}

When you use the tricky method to insert data into your ArrayList Collection, it doesn't match the specified type ie String , but still This is compatible because of Generics compatibility with older Legacy codes.

If it wouldn't have been for this ie if it would have been the same way as of arrays, then all of the pre-java generic code would have been broken and all the codes would have to be re-written.

Remember one thing for generics, All your type-specifications are compile time restrictions , so when you use the tricky method to insert data in your list reference, what happens is the compiler thinks of it as a list to which ANYTHING apart from primitives can be added.

Only if you would have written this:

...
public class Test {
    private static ArrayList tricky(ArrayList<String> list) {
        list.add(12345); //Error, couldn't add Integer to String
        return list;
    }
...
}

I have written a documented post on this, Read here .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM