9

Collections.copy is defined as:

public static <T> void copy(List<? super T> dest, List<? extends T> src)

I understand how it works and have no issues. I tried writing an alternative way to exhibit the same as:

public static <T, E extends T> void copy(List<T> dest, List<E> src){
        for(E e: src){
            dest.add(e);
        }//ignore implementation. it has flaws. The focus is declaration of copy
    }

The idea is simple. For whatever type you read from source, the destination type should be its super type. So now you can do:

copy(new ArrayList<Number>(), new ArrayList<Number>());
copy(new ArrayList<Number>(), new ArrayList<Integer>());
copy(new ArrayList<Object>(), new ArrayList<Number>());
copy(new ArrayList<Object>(), new ArrayList<Double>());

It looks alright. But is there any flaw with above when compared to actual Collections.copy? Any place where the actual outbeats the above one from type-information perspective?

Jatin
  • 31,116
  • 15
  • 98
  • 163

4 Answers4

3

I think one place where the original is better, is from conciseness. Especially from code-generation point of view.

If I am calling original function with full declaration, I can

Collections.<Object>copy(new ArrayList<Object>(), new ArrayList<Number>());

In my case it would be:

MyClass.<Object, Number> copy(new ArrayList<Object>(), new ArrayList<Number>());

So its a bit more concise.

Jatin
  • 31,116
  • 15
  • 98
  • 163
2

In terms of absolute information typing, I do not think there is a difference (but I would be happy to be proved wrong). In fact, it compiles and runs fine if the body of your static method is simply a call to Collections.copy. A couple key points though, after a lot of thought and experimentation:

  • The signature of Collections.copy is conceptually much more clear than the one you've suggested. It exemplifies the PECS principle (see also Josh Bloch's Effective Java).
  • Looking into source code (OpenJDK 7) of Collections.copy, I observe that the type parameter T is never actually used directly. Your version creates the same type bounding (source must generate a subtype of dest), but without such a clear connotation that there is some well-defined "middle type" where the wildcard bounds meet.
  • Perhaps most importantly of all, the only reason your version can be considered equivalent is because it's a static method, and static methods in general don't take full advantage of the power of generics (though that is no reason not to still use the best syntax there). The following example makes this very clear by doing similar operations as instance methods.

Consider a class TypeCopier<T> which copies parametrized types.

public class TypeCopier<T> {

  void copyType(List<? super T> dest, List<? extends T> src) {
    // copy
  }

  public static void main(String[] args) {
    TypeCopier<Number> copier = new TypeCopier<>();
    copier.copyType(new ArrayList<Object>(), new ArrayList<Integer>());
  }
}

Notice a few things about this first example:

  1. It was easy to specify the type Number with a single parameter
  2. It compiles
  3. It allows dest and src to be any supertype and subtype of Number respectively

Now try to make a similar class using the signature you suggested:

public class TypeCopierBad<T, E extends T> {

  void copyType(List<T> dest, List<E> src) {
    // copy
  }

  public static void main(String[] args) {
    TypeCopierBad<Object, Number> copier = new TypeCopierBad<>();
    copier.copyType(new ArrayList<Object>(), new ArrayList<Integer>());
  }
}

Notice a few things about this second example:

  1. It required two type parameters to try to specify the same thing as the one above (and it fails that attempt, as we will see below)
  2. It does not compile, because src is not exactly the Number type
  3. It much more tightly constrains what dest and src can be, compared to the previous example
Community
  • 1
  • 1
The111
  • 5,757
  • 4
  • 39
  • 55
1

Looks like your declaration meets the requirement of PECS principle, so i think it's just about style, where Collections.copy is more common version.

Dmytro
  • 1,850
  • 3
  • 14
  • 20
0

Suppose you have Objects in both ArrayLists. And you iterate over source and add each object from source to destination. The problem is that, each object that you take from source and add to destination will be pointing to same memory location. For example, Say

source {x, y}
destination {x, y}.

In this case, x in both source and target will be pointing to same memory location

niyasc
  • 4,440
  • 1
  • 23
  • 50
  • Firstly, the question as mentioned is not per implementation details. But only from type information perspective. On what kind of inputs can original take and mine cant. Secondly, there is absolutely nothing wrong with both pointing to same object. In fact they are the same object and are meant to point to the same. – Jatin Feb 16 '15 at 11:33