Why is Stream.sorted not type-safe in Java 8?

  • A+
Category:Languages

This is from the Stream interface from Oracle's implementation of JDK 8:

public interface Stream<T> extends BaseStream<T, Stream<T>> {     Stream<T> sorted(); }  

and it is very easy to blow this up at run time and no warning will be generated at compile time. Here is an example:

class Foo {     public static void main(String[] args) {         Arrays.asList(new Foo(), new Foo()).stream().sorted().forEach(f -> {});     } } 

which will compile just fine but will throw an exception at run time:

Exception in thread "main" java.lang.ClassCastException: Foo cannot be cast to java.lang.Comparable 

What could be the reason that the sorted method was not defined where the compiler could actually catch such problems? Maybe I am wrong but isn't it this simple:

interface Stream<T> {     <C extends Comparable<T>> void sorted(C c); } 

?

Obviously the guys implementing this (who are light years ahead of me as far as programming and engineering is considered) must have a very good reason that I am unable to see, but what is that reason?

 


Essentially, you're asking if a method can require the class' type parameter be more strict for only said method. This is not possible in Java. There is no way to tell the compiler, "hey, this one method requires that the type parameter match more specific bounds than defined at the class level". Such a feature may be useful but I'd also expect confusing and/or complicated.

There's also no way to work around this with the way generics is currently implemented. For instance, you were wondering why something like the following wouldn't work:

public interface Stream<T> {      <C extends Comparable<? super T>> Stream<T> sorted(Class<C> clazz);  } 

The problem is there's no guarantee that Class<C> is assignable from Class<T>. What if you had the following:

public class Foo implements Comparable<Foo> { /* implementation */ } 

public class Bar extends Foo {} 

public class Other extends Foo {} 

You could basically tell the Stream that it's a Stream of Bar elements but sort as if it was a Stream of Other elements:

Stream<Bar> stream = barCollection.stream().sorted(Other.class); 

And, if casting is done via the given Class, you'd still have ClassCastExceptions at runtime. If the Class wasn't utilized for casting then the argument is useless since it adds no type-safety.

You also can't do something like:

public interface Stream<T> {      <C extends T & Comparable<? super T>> Stream<T> sorted(Class<C> clazz);  } 

Because <C extends T & ...> is a compile-time error; type parameters cannot be followed by other bounds. Even if this was allowed I'm not sure if it'd be effective.


I'd also like to point out the Stream.sorted(Comparator<? super T>) isn't type-safe because of Stream's method signature. It's type-safe because the Comparator enforces the correct types. In other words, it's the Comparator that makes sure the elements can be compared, not the Stream.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: