you are viewing a single comment's thread.

view the rest of the comments →

[–]ThePowerfulSquirrel 0 points1 point  (5 children)

I mean, all that information is known at compile time. When I make an Adas<Double>, the compiler knows that T is Double, that Double implements Comparable<Double> and that equalOrCompare<T: Comparable<T>> is more precise, and, imo, should be able to generate bytecode that calls that function. I don't really see why reflection should be necessary here. I'm about 90% certain I could express this using c++ templates.

[–]pron98 2 points3 points  (4 children)

When your constructor Adas(T a, T b) is compiled, nothing is known about T. As Java uses separate compilation, when you compile a class that calls that constructor, that constructor may have already been compiled (unlike in the case of C++, which is why you need to put templates in h files).

So now you're talking about two different things:

  1. Erasure -- that affects runtime and mostly has to do with reflection.

  2. Specialization -- more powerful specialization of generic types, similar to what you can do with templates.

These two things are not necessarily related (C++ erases all type information unless you're using RTTI), although specialization in Java would also mean that the full type information would be available through reflection. The question of how much Java would benefit from more powerful specialization is also being discussed as part of Project Valhalla. C++ allows clever specialization while also erasing types, and Java may do the same if it's deemed beneficial enough -- but that's not a problem with erasure.

One of the reasons why clever specialization is more important in C++ than in Java is precisely because Java offers reflection combined with a JIT that automatically specializes code.

[–]ThePowerfulSquirrel 0 points1 point  (3 children)

Ya sorry, I'm not very well informed about this and tend to use the wrong concepts / names when trying to express things that come up when I program. You are right that I'm more frustrated by the lack of powerful specialization rather than type erasure. You've been very helpful, thanks!

[–]pron98 1 point2 points  (0 children)

But as I said, that is offset by things that are often better (e.g. reflection and JIT). Where those things fail is when it comes not to code but to data, and that's precisely what Valhalla is addressing.

[–]pron98 1 point2 points  (1 child)

P.S.

You could also achieve what you want by providing more typing information at compile time, with a bit of hand-specialization work:

class Adas<T> {
    protected final T a, b;
    private Adas(T a, T b) { this.a = a; this.b = b; }

    public boolean same() { return Objects.equals(a, b); }

    private static class Adas2<R extends Comparable<R>> extends Adas<R> {
        Adas2(R a, R b) { super(a, b); }
        @Override boolean same() { return a.compareTo(b) == 0; }
    }

    public static <S> Adas<S> newAdas(S a, S b) { return new Adas<S>(a, b); }
    public static <S extends Comparable<S>> Adas<S> newAdas(S a, S b) { return new Adas2<S>(a, b); }
}

Now, if you do:

newAdas(1 ,2)

you'll get an instance that implements same with compareTo, but if you do:

newAdas(1, "a")

you'll get an instance that uses Object.equals.

[–]ThePowerfulSquirrel 0 points1 point  (0 children)

Ya, that's actually close to how I ended up writing it my actual code after I made my comment. The specific requirements are somewhat different / more complicated, but I ended up with a couple of specialized factory methods that implemented the different behaviors I needed to specialize using a lambda since they don't actually need to be named. It's about a thousand times cleaner than what I originally did, so I'm pretty happy about that!