- List<T> doesn't "just work" with non-reference types. It needs boxing that increases memory usage and introduces stuff like ints being null.
- We need special functional interfaces for non-reference types for that reason (e.g. IntConsumer).
- This also affects Stream<T>, so we need IntStream etc.
- A method must have a parameter of that generic type (or it has to belong to a class that has this generic type). It otherwise becomes indistinguishable during rumtime. For example, a method like ImmutableList.CreateBuilder<T>() is not possible in Java (that example is from C#'s collection types).
Type erasure moslty comes into play when looking at non-reference types. For reference types, it seems to work pretty good (although it's weird that Map<String, String> will have the same runtime type as Map<Object, Object>). The last point I mentioned is not good, but no deal breaker. If generics would be like in .NET, we wouldn't have any of these restrictions.
With type erasure, we ironically have to write more java code while not being able to express stuff in an abstract manner (Stream<T> is incompatible with IntStream).