14

Why do we lose type safety when using List and not while using List<Object>? Aren't they basically the same thing?

EDIT: I found that the following gives a compilation error

public class TestClass
{
    static void func(List<Object> o, Object s){
        o.add(s);
    }

    public static void main(String[] args){
        func(new ArrayList<String>(), new Integer(1));
    }
}

whereas this doesn't

public class TestClass
{
    static void func(List o, Object s){
        o.add(s);
    }

    public static void main(String[] args){
        func(new ArrayList<String>(), new Integer(1));
    }
}

Why?

highlycaffeinated
  • 19,729
  • 9
  • 60
  • 91
Varun Achar
  • 14,781
  • 7
  • 57
  • 74
  • 2
    The direct answer to why does the second one compile is so that code written before java 5 can be compiled by 5 and later. – Affe Jul 21 '11 at 22:35

6 Answers6

7

List is a list of some type you don't know. It could be a List<String>, List<Integer>, etc.
It's effectively equivalent to List<?>, or List<? extends Object>, except that it doesn't document that fact. It's only supported for backwards compatibility.

List<Object> is a list of Objects. Any object of any type can be put inside it, contrary to a List<String>, for example, which only accepts strings.

So no, they're not the same thing.

user541686
  • 205,094
  • 128
  • 528
  • 886
5

Why do we lose type safety when using List and not while using List<Object>? Aren't they basically the same thing?

No they are not the same thing.

If you are providing an API,

class API {
  static List<Object> getList() { ... }
  static void modifyList(List<Object> l) { ... }
}

and a client uses it improperly

List<Integer> list = API.getList();
API.modifyList(list);
for (Integer i : list) { ... }  // Invalid

then when your API specifies List<Object> they get a compile-time error, but they don't when API.getList() returns a List and API.modifyList(list) takes a List without generic type parameters.

EDIT:

In comments you mentioned changing

void func(List<Object> s, Object c) { s.add(c); }

to

void func(List s, Object c) { s.add(c); }

so that

func(new List<String>(), "");

would work.

That is violating type safety. The type-safe way to do this is

<T> void func(List<? super T> s, T c) { s.add(c); }

which is basically saying that func is a parameterized function that takes a List whose type can be any super class of T, and a value of type T, and adds the value to the list.

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
  • Yes, I found a similar case. The program doesn't compile when i have this: `func(List s, Object c){ s.add(c)}` and call `func(new ArrayList(), new Integer(1));`. It compiles when I change the method to `func(List s, Object o)` and call it again in the same way – Varun Achar Jul 21 '11 at 22:14
  • Without generic type, the for loop would not compile as well. – user802421 Jul 21 '11 at 22:18
  • @Varun, a `List` is not a `List` because `myList.add(Boolean.FALSE)` is valid when `myList` is a `List` but not when it is a `List`. Maybe your function should be ` void func(List super T> s, T c) { s.add(c); }` – Mike Samuel Jul 21 '11 at 22:23
  • "The type-safe way to do this is..." It is sufficient to say ` void func(List s, T c) { s.add(c); }` – user102008 Aug 31 '11 at 03:09
  • @user102008, true. That is not as general though. You cannot then do `List heterogeneousList = ...; func(heterogeneousList, "foo");` because the `"foo"` makes `T` bind to `String` at that call-site, but the `hetergeneousList` has a type of `List` which is not a `List`. If I was trying to be as general as possible though, I should've probably used a `Collection` instead of a `List`. – Mike Samuel Aug 31 '11 at 17:06
  • @Mike Samuel: Yes you can. Try it. – user102008 Aug 31 '11 at 20:43
  • @user102008, You're right. The `` in your signature binds to the type-bottom of (`String`, `Object`) which is (`Object`), but in my signature `` binds to `String`. I was mistakenly assuming it would fail because I was assuming `` would be `String` in both cases. – Mike Samuel Aug 31 '11 at 21:57
3

A List<Object> isn't really any more typesafe than a List. However, the Object in the code does imply intent. When someone else looks at it later, they can see that you purposefully chose Object as the type, rather than wondering if you just forgot to put a type or are storing something else and typecasting it elsewhere.

Since code gets read more than it gets written, hints at the intent of the code can be very valuable later on.

RHSeeger
  • 16,034
  • 7
  • 51
  • 41
1

The reason you have a compiler warning when you use List instead of List<Object> is that when you have a List the compiler doesn't know what type of List it is, so while you could treat it as a List<Object>, the compiler can't ensure that at some other point in the code it wasn't set to reference a List<String> and the type safety of the Generics cannot be checked. That is really the compiler warning here - it is saying it can't help ensure the type safety of the generics, and it won't happen at runtime either (until at some later point there is an actual cast in the code).

Yishai
  • 90,445
  • 31
  • 189
  • 263
0

Type Erasure is one answer and the backward compatibility to pre Java 1.5 and tighter type check in case of first one.

Generics were introduced to the Java language to provide tighter type checks at compile time and to support generic programming. To implement generics, the Java compiler applies type erasure to:

Replace all type parameters in generic types with their bounds or Object if the type parameters are unbounded. The produced bytecode, therefore, contains only ordinary classes, interfaces, and methods. Insert type casts if necessary to preserve type safety. Generate bridge methods to preserve polymorphism in extended generic types. Type erasure ensures that no new classes are created for parameterized types; consequently, generics incur no runtime overhead.

Anil Singh
  • 21
  • 5
0

For purposes here, you could say they're the same thing. But presumably you almost never actually fill a List with pure instance of Object. They're Strings or something. In this example, List<Object> is technically using generics but not really taking any advantage of it. So, it loses the compile-time type checking of generics.

Sean Owen
  • 66,182
  • 23
  • 141
  • 173