

Java Generics FAQs - xvirk
http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html

======
eridal
TIL why

    
    
        class Foo<T> {
    
            void test(Foo<T> f) {
                // ...
            }
    
            static void boom() {
                Foo<?> f = new Foo<String>();
                f.test(f); // <--- boom! compile error
            }
        }

~~~
peeters
There isn't really a good reason. The compiler just isn't sophisticated enough
to support it.

You could always write a generic method to do it for you:

    
    
        static <T> void testFoo(Foo<T> f) {
           f.test(f);
        }
    
        static void boom() {
           Foo<?> f = new Foo<String>();
           testFoo(f); // works just fine
        }
    

So the problem isn't that what you're doing is fundamentally unsafe, it's just
that Java has no syntax to capture `?` except through a generic type parameter
on a method or class.

~~~
plesner
Actually, it's not in general decidable whether f.test(f) is safe because f
gets read twice and the value might change in the meantime. In the example a
simple flow analysis will tell you it doesn't but the general case is
equivalent to the halting problem. The generic method solves the problem
because now there is only one read in boom() and the concrete type in testFoo
ensures that f can't change in an incompatible way between reads.

A more sophisticated type system could allow access if it can be proven safe
with simple flow analysis but wildcards are already complicated, that would
make them much more so for not much benefit.

~~~
twic
> Actually, it's not in general decidable whether f.test(f) is safe because f
> gets read twice and the value might change in the meantime.

Might it? I can't see any way that actions in the current thread can change
the value of f in between those two uses. Actions in another thread can be
ignored until the receiving end of a happens-before relationship is
encountered, and there isn't such a point inside that expression.

More generally, yes, two uses of the same wildcarded variable in one method
might not yield objects with the same actual type argument. But in this case i
think it's safe.

~~~
plesner
What I said didn't really make sense -- f.test(f) is a concrete example so how
am I generalizing? What I meant was: in the general case of expressions that
contain multiple references to the same variable it might, for instance

f.testTwo(f = newF, f)

------
uv3d779b
I love Java-style generics! Any other popular languages have anything similar?
Besides C# of course.

~~~
Devthrowaway80
I can't stand Java generics. They're better than nothing, but type erasure
really limits their utility.

~~~
tel
Why do people find that type erasure is a problem? I'm happy, for instance,
writing in languages wherein type erasure is the obvious right answer.

~~~
_asummers
I don't often find myself needing to get T, but sometimes I do. Consider the
case of a Handler<T extends Foo> interface that several classes implement. If
I have several of these, for various Foo subtypes, I might want to put them in
some sort of collection that I can look into later after receiving a Foo
message, so that I can dispatch it appropriately. Why do I have to do this
dispatch myself? The types of all the Handlers go away, so I need to hold on
to them somehow, and match at runtime.

The clear way to do this is to put them in a Map<Class<T>, Handler<T>>. Now
how do these Handler objects declare that they're ONLY able to handle a
specific Foo subtype (let's call it FooBar)? It'd be REALLY nice if I could
just say "Hey handler, what's your generic type? Is the message I just got an
instanceof your generic type?" Java won't let you do this due to erasure.

Okay we can get around this. Each Handler<T> has to declare a method (say
getType()) that returns the Class<T>. Since I'm generically declaring my class
FooBarHandler<FooBar>, I can protect myself from returning a BazBat in this
method, but there's NO way for me to abstract this method away. Each Handler
has to declare a "public Class<T> getType()" that returns T.class. But since T
isn't a thing past compile time, I have to repeat this same method
implementation for each concrete type. Gross. This has the added irritation of
forcing all the parent classes, if they implement this interface, to be
abstract, since they can't implement the method appropriately. This isn't
necessarily a bad thing, but the limitation is annoying.

In an ideal world, I can declare some ParentHandler<T> that has this method,
and have all my handlers just extend it, with no duplication.

~~~
zak_mc_kracken
> It'd be REALLY nice if I could just say "Hey handler, what's your generic
> type? Is the message I just got an instanceof your generic type?

No, it wouldn't be nice, it would be unsafe. If one day you end up adding a
new type in your container, you need to update your runtime check as well or
your code will fail in mysterious ways.

Erasure keeps you honest by asking you to think carefull about the types so
that they can be checked by the compiler, and once the compiler has done this
verification, you are guaranteed that your code will work.

Any language feature that encourages the use of reflection , such as
reification of types, should not be supported by a language that wants to
claim to be sound.

~~~
tel
It's not necessarily unsafe, but it is very difficult to do safely. The design
suggested is certainly unsafe, however---there's no way to ensure that the
values don't lie about their self-reported type and so using that to trigger a
coercion is liable to explore things.

If the compiler provides a couple things:

    
    
        data TypeRepr
        typeReprEq :: TypeRepr -> TypeRepr -> Bool
        
        typeOf :: Typeable a => a -> TypeRepr
    

such that TypeRepr cannot be generated (e.g. faked) by users, typeOf is
guaranteed to be genuine, and (this is the hardest) such a thing doesn't
violate parametricity then you can use that interface to write

    
    
        safeCoerce :: (Typeable a, Typeable b) => a -> Maybe b
    

which is guaranteed to only allow the coercion when `a` and `b` actually
happen to be the same type.

------
halosghost
Public, bare getters/setters! Hisss!!!!!

To elaborate, this is one of the most common annoyances I have with Java
ecosystems. In an attempt to make everything Objectionable (ho-ho!), coders
write classes with their internal data structure marked as private but then
make getters and setters (which do nothing other than set or retrieve the
value) which are public.

The result is that their internal data structure is actually public (since its
advertised through the getters/setters), but with the added bonus of extra
function calls and member offset resolutions (i.e., it makes execution slower
while single-handedly destroying the main benefit that OOP offers—backwards
compatibility through isolation of the internal data structure).

While it doesn't surprise me to see new-to-OOP coders making this kind of
mistake, I find it troubling that many FAQs/tutorials/classes actually
advocate for these patterns.

On the bright side, the web page looks nice :)

~~~
lemming
Their internal data structure is not public, since the details of how the
value returned by the getter is calculated can be changed later, transparently
to calling code. This is the point of encapsulation. Getters and setters can
be made to access member variables in thread-safe ways, which you can't do
with bare member fields. If you start with bare member fields and then
suddenly realise that you need to encapsulate (for example, to obey some
internal concurrency invariant) you have a major refactoring on your hands. If
the clients of that code are not under your control, you're screwed.

The performance impact is total FUD - the JVM eats that stuff for breakfast.

~~~
halosghost
Well, actually, since the getters/setters advertise the type of the internal
data structure, it is public. True, you could change the return type of them
(which would require you to do just as much refactoring as if you changed a
public internal data structure) or change the internal data structure and cast
to the return type but many times that will make the getter/setter
meaningless.

This is all not to mention that since the setter is public, you don't have any
protection of your internal data structure anyway since anyone can modify it.

It's true that getters/setters offer thread-safety; I'm happy to concede that.

However, the performance impact is not FUD (or at least, I did not mean it as
such); ask any systems programmer and they will happily tell you that one of
the most common optimizations you can make is removing function calls where
they are unneeded. Another comment mentioned that the JVM can JIT this problem
away through inlining; I honestly do not know if that it is true—if it is,
then great; that would be a significant benefit over this same kind of
formulation in, say, Cxx.

Finally though, I don't really understand why Java programmers cling to
backwards compatibility so when their own language shows you what happens when
you commit to never removing anything to avoid breakage (i.e., you build in a
huge amount of cruft).

Keep in mind, none of my posts are meant to be flamey here; I was just giving
my opinion of one of the patterns displayed in the FAQ that I find
distressing.

~~~
lemming
_since the getters /setters advertise the type of the internal data structure_

No, they don't - that's the whole point. You can change the type of your
internal data structure however you want, but as long as the getters and
setters accept and return the same type, your contract remains the same with
your clients.

 _since the setter is public, you don 't have any protection of your internal
data structure anyway since anyone can modify it_

Of course you do. You can check invariants in your setter before applying any
changes, and throw an exception if the passed value violates them. You can
take a lock in your setter, to ensure that the passed information is applied
in a thread-safe way. Again, this is the whole point of encapsulation.

 _Another comment mentioned that the JVM can JIT this problem away through
inlining; I honestly do not know if that it is true—if it is, then great_

The JVM can do this, and much much more, at runtime. Basing your optimisation
advice on what a systems programmer might have told you 20 years ago is a
really bad idea. All good JITs and compilers have inlined small functions for
a very long time now. The JVM is particularly impressive in that it can do
that for virtual calls too.

------
dcposch
I think this illustrates one reason why Go doesn't have generics.

Generics add _a lot_ of complexity to a language. There are two main ways to
implement them that I know of--the Java style, with type erasure, and the C++
style, where you essentially generate code for each instantiation--both have
significant drawbacks. Either way, it complicates the syntax, complicates the
type checker, complicates all the tooling around the language (IDE support,
debugger, profiler, linter).

OTOH, if you don't have generics, you either just use concrete types or you
essentially do type erasure yourself, explicitly (eg with interface{} in Go or
void* in C). It costs you a few casts and maybe some code duplication.

In return, you get a simple language, where it's easier to write robust
tooling.

I think erring on the side of simplicity and avoiding generics altogether is
more than worth it.

~~~
ane
Java generics aren't the best example of how to implement generics. C# or Rust
are better in this regard, which both have reified generics. Generics _do_
solve a practical problem identified decades ago, ad-hoc code generation or
void* aren't the solution. I don't think the omission of generics has any
bearing on how easy writing tooling is.

You can always omit them, if you're prepared to deal with code duplication,
but I think you're downplaying its significance: it is not just "maybe some"
code duplication, nor is it a "a few casts". It's sometimes a deep, murky
swamp you're forced to trudge in, and it's not pleasant.

~~~
useerup
To be fair, generics are only needed for as long as you insist on static
typing. Languages with dynamic typing where types are checked at runtime do
not need generics.

And generics _is_ complicated topic. I am all for static typing, especially
for large code bases. But sometimes when I venture too far out in generic land
I find myself wondering if it is worth it? A well-designed library that uses
generics prudently can be a bliss to use. But the implementation code can get
near-unreadable.

------
DavidPlumpton
I was dealing with this a couple of days ago: List<List<List<Map<String,
Object>>>>

Sigh.

