Asked  7 Months ago    Answers:  5   Viewed   45 times

Why isn't Collection.remove(Object o) generic?

Seems like Collection<E> could have boolean remove(E o);

Then, when you accidentally try to remove (for example) Set<String> instead of each individual String from a Collection<String>, it would be a compile time error instead of a debugging problem later.

 Answers

39

Josh Bloch and Bill Pugh refer to this issue in Java Puzzlers IV: The Phantom Reference Menace, Attack of the Clone, and Revenge of The Shift.

Josh Bloch says (6:41) that they attempted to generify the get method of Map, remove method and some other, but "it simply didn't work".

There are too many reasonable programs that could not be generified if you only allow the generic type of the collection as parameter type. The example given by him is an intersection of a List of Numbers and a List of Longs.

Tuesday, June 1, 2021
 
superhero
answered 7 Months ago
23

From JLS 4.8 Raw Types

The use of raw types is allowed only as a concession to compatibility of legacy code. The use of raw types in code written after the introduction of generics into the Java programming language is strongly discouraged.

and

The type of a constructor (§8.8), instance method (§8.4, §9.4), or non-static field (§8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.

Which - if you read it carefully - implies that all types are erased, not just the type you left out.

Tuesday, June 1, 2021
 
Santi
answered 7 Months ago
16

It was a Java design decision, and one that some consider a mistake. Containers want Objects and primitives don't derive from Object.

This is one place that .NET designers learned from the JVM and implemented value types and generics such that boxing is eliminated in many cases. In CLR, generic containers can store value types as part of the underlying container structure.

Java opted to add generic support 100% in the compiler without support from the JVM. The JVM being what it is, doesn't support a "non-object" object. Java generics allow you to pretend there is no wrapper, but you still pay the performance price of boxing. This is IMPORTANT for certain classes of programs.

Boxing is a technical compromise, and I feel it is implementation detail leaking into the language. Autoboxing is nice syntactic sugar, but is still a performance penalty. If anything, I'd like the compiler to warn me when it autoboxes. (For all I know, it may now, I wrote this answer in 2010).

A good explanation on SO about boxing: Why do some languages need Boxing and Unboxing?

And criticism of Java generics: Why do some claim that Java's implementation of generics is bad?

In Java's defense, it is easy to look backwards and criticize. The JVM has withstood the test of time, and is a good design in many respects.

Saturday, June 5, 2021
 
StampyCode
answered 7 Months ago
46

It depends on what you need to do. You need to use the bounded type parameter if you wanted to do something like this:

public <T extends Shape> void addIfPretty(List<T> shapes, T shape) {
    if (shape.isPretty()) {
       shapes.add(shape);
    }
}

Here we have a List<T> shapes and a T shape, therefore we can safely shapes.add(shape). If it was declared List<? extends Shape>, you can NOT safely add to it (because you may have a List<Square> and a Circle).

So by giving a name to a bounded type parameter, we have the option to use it elsewhere in our generic method. This information is not always required, of course, so if you don't need to know that much about the type (e.g. your drawAll), then just wildcard is sufficient.

Even if you're not referring to the bounded type parameter again, a bounded type parameter is still required if you have multiple bounds. Here's a quote from Angelika Langer's Java Generics FAQs

What is the difference between a wildcard bound and a type parameter bound?

A wildcard can have only one bound, while a type parameter can have several bounds. A wildcard can have a lower or an upper bound, while there is no such thing as a lower bound for a type parameter.

Wildcard bounds and type parameter bounds are often confused, because they are both called bounds and have in part similar syntax. […]

Syntax:

  type parameter bound     T extends Class & Interface1 & … & InterfaceN

  wildcard bound  
      upper bound          ? extends SuperType
      lower bound          ? super   SubType

A wildcard can have only one bound, either a lower or an upper bound. A list of wildcard bounds is not permitted.

A type parameter, in constrast, can have several bounds, but there is no such thing as a lower bound for a type parameter.

Quotes from Effective Java 2nd Edition, Item 28: Use bounded wildcards to increase API flexibility:

For maximum flexibility, use wildcard types on input parameters that represent producers or consumers. […] PECS stands for producer-extends, consumer-super […]

Do not use wildcard types as return types. Rather than providing additional flexibility for your users, it would force them to use wildcard types in client code. Properly used, wildcard types are nearly invisible to users of a class. They cause methods to accept the parameters they should accept and reject those they should reject. If the user of the class has to think about wildcard types, there is probably something wrong with the class's API.

Applying the PECS principle, we can now go back to our addIfPretty example and make it more flexible by writing the following:

public <T extends Shape> void addIfPretty(List<? super T> list, T shape) { … }

Now we can addIfPretty, say, a Circle, to a List<Object>. This is obviously typesafe, and yet our original declaration was not flexible enough to allow it.

Related questions

  • Java Generics: What is PECS?
  • Can someone explain what does <? super T> mean and when should it be used and how this construction should cooperate with <T> and <? extends T>?

Summary

  • Do use bounded type parameters/wildcards, they increase flexibility of your API
  • If the type requires several parameters, you have no choice but to use bounded type parameter
  • if the type requires a lowerbound, you have no choice but to use bounded wildcard
  • "Producers" have upperbounds, "consumers" have lowerbounds
  • Do not use wildcard in return types
Tuesday, June 15, 2021
 
dotoree
answered 6 Months ago
39

Prefer Collection (or List, or Set as appropriate) to an array. With generics you get the type-checking that was lacking pre-Java 5. Also, by exposing only the interface, you are free to change the implementation later (e.g. switch an ArrayList for a LinkedList).

Arrays and generics don't mix very well. So, if you want to take advantage of generics, you should usually avoid arrays.
I.e: You can't generically create an array. For example, if T is a generic type then "new T[0]" doesn't compile. You'd have to do something like "(T[]) new Object[0]", which generates an unchecked cast warning. For the same reason, you can't use generic types with varargs without warnings.

Using Collections.unmodifiableCollection (and similar methods), you get the read-only constraint (which you can't achieve with an array - you would have to return a clone of the array).

You can't enforce immutability of members, but then you can't do that with an array either.

Saturday, September 18, 2021
 
Kasun Sandaruwan
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share