4 ms·
Does anyone know of a good blog post or article that explains why Java's type erasure approach to generics is considered problematic? I'd be interested to read
by alexhutcheson 8y ago
Does anyone know of a good blog post or article that explains why Java's type erasure approach to generics is considered problematic? I'd be interested to read about the specific problems it causes.
- bunderbunder 8y agoIt's a bunch of small things, none of which are showstopping, all of which are annoying. You can't have these two functions co-existing in the same class: void Frob(List<String> someList) { ... } void Frob(List<Integer> someList) { ... } because, after erasure, they have identical signatures. You've got to repeat yourself a bunch, because generic methods don't know their own type parameters. So, to take an example using the Jackson JSON-handling library: MyClass foo = objectMapper.readValue(json, MyClass.class); is a bit annoying if you're coming from a language like C#, where you'd expect to be able to write code more like: MyClass foo = objectMapper.readValue(json); Generic type instantiations aren't really types in Java, they're more this sort of type-ish concept. List<String> and List<Integer> exist as distinctions made by Java's type checker, but their actual class is just List - there's no such thing as List<String>.class. That's what's going on with that method overload example above. It's also the reason that this won't even compile, and you've got to resort to hacky workarounds in this sort of situation: Node<MyClass> foo = objectMapper.readValue(json, Node<MyClass>.class); Then there's all the stuff about boxing, and how you can't have generic collections of atomic values, only boxed values. Which is responsible for a lot of excess memory consumption and pointer chasing, and also for things like that whole extra side quest in Java 8 streams where you get classes like IntStream and DoubleStream (but not FloatStream).
- Matthias247 8y agoBesides those you also can't do something like Object xyz = ...; if (xyz instanceof List<String>) { doA(); } else if (xyz instanceof List<Integer> { doB(); } because there is only a single List type at runtime.
- bunderbunder 8y agoAs long as you are able to take the same code path for all empty lists, you can do something like if (xyz instanceof List) { if (xyz.isEmpty()) { ... } else if (xyz.get(0) instanceof String) { ... } else if (xyz.get(0) instanceof Integer) { ... } } Which, I admit, is a gross hack.
- int_19h 8y agoAnd, conversely, you can't implement both List<String> and List<Integer> on the same class.
- bunderbunder 8y agoThough, even in C#, it would be inadvisable. If you have the same class implement both `IList<String>` and `IList<int>` you'll have some ambiguity to deal with in implementing object IList.this[int index] { ... } The expectation is that that property's getter should just upcast the return value of the generic version of the indexer. For both languages, you'd be better off having two separate methods for the two different kinds of list.
- int_19h 8y agoThe point of doing this is not to invoke these members directly on the object, though. It's to allow the object to be passed to anything that expects either IList<string> or IList<int>, or for runtime discovery purposes with is/as. Perhaps a better example is something like IConvertibleTo<T>. There's an obvious meaning to a class implementing it for many different Ts.