Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Modified Collections.copy

Tags:

java

generics

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?

like image 889
Jatin Avatar asked Feb 16 '15 11:02

Jatin


People also ask

Is collections copy deep copy?

The collection. copy( ) method returns a deep copy of the collection instance. Deep copy means that objects or collections within the original collection are duplicated and do not share any reference with the returned collection.

What is copy collection?

The copy() method of java. util. Collections class is used to copy all of the elements from one list into another. After the operation, the index of each copied element in the destination list will be identical to its index in the source list. The destination list must be at least as long as the source list.


3 Answers

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.

like image 89
Jatin Avatar answered Oct 06 '22 00:10

Jatin


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
like image 25
The111 Avatar answered Oct 06 '22 00:10

The111


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.

like image 31
Dmytro Avatar answered Oct 06 '22 01:10

Dmytro