4 ms·
IMO C# got this right. Admittedly I prefer C#, but working in Java right now type erasure really confuses me, especially working with streams. I can/will learn
by overgard 5y ago
IMO C# got this right. Admittedly I prefer C#, but working in Java right now type erasure really confuses me, especially working with streams. I can/will learn but it seems like a bad model.
- MBCook 5y agoAs the article says, it was an unfortunate side effect of trying to maintain compatibility. I imagine C# was able to learn from what was going on with Java at the time and not have to suffer the same fate. Or maybe it was just less popular and could force the issue (I’ve never dealt with it so I don’t know the history well).
- kgeist 5y agoThe main use case of generics is collections, and if my memory serves me right, in .NET they simply created a new collections library (System.Collections.Generics) leaving the original intact (System.Collections) allowing old programs to work as before. It lacked 100% compatibility because you couldn't freely interchange the classes (without writing adapters) but from what I gathered, it was a small price to pay compared to type erasure (in my opinion) which prevented more aggressive runtime optimizations/evolution. Today you usually find the old collections in ancient software which hasn't been updated for years.
- kaba0 5y agoAs it is usually brought up, not erasing types also comes with some potential cons: namely making CLR languages dependent on C#’s chosen variance model. In Java, List<Cat> may or may not be the subclass of List<Animal>, but it is up to the List implementor. This way Scala/Kotlin/another JVM language is free to define their own variance model independent of the host language. C# did limit their language ecosystem with it quite a bit. (Afaik Scala for CLR stopped in part due to this).
- lokedhs 5y agoList<Cat> cannot be a subclass of List<Animal> as that would imply I can do list.add(dog) into that list. You probably know this, but I'm replying in the hope it clarifies things to other readers. The way it's expressed in Java is List<? extends Animal>
- kaba0 5y agoYeah, java can change the variance model used by generics, but my point is that it is a language-level feature, not something fundamental at a JVM level, which is imo the correct decision. Also, unfortunately arrays are covariant so Cat[] is a subclass of Animal[] (both in Java and C# actually), where your mentioned example indeed introduces a “poisoned” value, waiting for a classcastexception.
- kgeist 5y agoHow do two languages with two different variance models interoperate in JVM if they share the same type but expect different behavior? Is it safe to share a list created in one language and pass into another if their variance models differ? Having an explicit variance model makes cross-language interoperability safer and easier (which was one of the main selling points of the Common Language Runtime), doesn't it?
- kaba0 5y agoSimply from an empirical point of view, the CLR is pretty much a desert compared to the flourishing JVM one so while it can be attributed to many things, I am really sure that explicit variance is not that attractive for language developers.
- jayd16 5y agoIn C# I believe you can optionally mark generic classes/methods as covariant or contravariant. Is that not enough or does it not get exposed in the CLR or something? https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/in-generic-modifier https://docs.microsoft.com/en-us/dotnet/csharp/language-refe... https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/out-generic-modifier https://docs.microsoft.com/en-us/dotnet/csharp/language-refe...
- hashmash 5y agoBrian Goetz's take on generics: http://cr.openjdk.java.net/~briangoetz/valhalla/erasure.html http://cr.openjdk.java.net/~briangoetz/valhalla/erasure.html
- Joker_vD 5y ago> The language actually provides quite a strong safety guarantee for generics, as long as we follow the rules: If a program compiles with no unchecked or raw warnings, the synthetic casts inserted by the compiler will never fail. Huh. That was written in 2020, four years after it was shown how to write a very small program that "compiles with no unchecked or raw warnings", and yet "the synthetic casts inserted by the compiler" will fail at run time [0]: class Unsound { static class Constrain<A, B extends A> {} static class Bind<A> { <B extends A> A upcast(Constrain<A, B> constrain, B b) { return b; } } static <T, U> U coerce(T t) { Constrain<U, ? super T> constrain = null; Bind<U> bind = new Bind<U>(); return bind.upcast(constrain, t); } public static void main(String[] args) { String zero = Unsound.<Integer, String>coerce(0); } } [0] N. Amin, R. Tate "Java and Scala's type systems are unsound: the existential crisis of null pointers", https://dl.acm.org/doi/pdf/10.1145/2983990.2984004 https://dl.acm.org/doi/pdf/10.1145/2983990.2984004
- hashmash 5y agoThe sample program doesn't compile: Unsound.java:16: error: method upcast in class Bind<A> cannot be applied to given types; return bind.upcast(constrain, t);
- overgard 5y agoThat's definitely interesting, but I feel like C#'s approach of just creating a new set of containers for generics aged way better. It feels silly that I'm paying a tax in 2021 on a decision made in 2004 to be backwards compatible to code written in like 1996