1

Does Java has generics deduction like C++ templates? For example:

class Foo<T> {}
class Foo<T extends Foo> {}

Which class is used depends on what T is actually is.

If Java do not have this feature, how I can deal with this problem(that is, different type of T may have different behaviours)?

rezaebrh
  • 424
  • 2
  • 6
  • 19
  • https://stackoverflow.com/help/how-to-ask – papaya Jan 06 '20 at 08:42
  • 5
    "How can I deal with this problem" the problem is in how you're framing it :) Generics are simply a way of omitting casts that the compiler can insert for you. If you can't write something with an explicit cast, you can't do it with generics. Many moons ago, I was a C++ programmer, and I occasionally rail against the fact you can't "specialize" in Java; you simply have to adjust the mindset. If `T` may have different behaviors, it's `T` that has to have the differing behaviors. – Andy Turner Jan 06 '20 at 08:45
  • 3
    @SiddarthSreeni what's wrong with this question? –  Jan 06 '20 at 08:47
  • 3
    @reavenisadesk it's very vague, and sort-of the [XY problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem): you're asking about how you've tried to solve it (saying how you'd solve it if it were C++); but you've not said what you're trying to solve. – Andy Turner Jan 06 '20 at 08:50
  • You can't have both those declarations in the same package in Java. Unclear what you're asking. – user207421 Jan 06 '20 at 08:51
  • 1
    Hi @AndyTurner, do you mean that in Java, a template parameter which has potentially different behavior is a ill design, and programmer should re-consider the design pattern? –  Jan 06 '20 at 08:51
  • @reavenisadesk the declarations you've shown there make it look like you want one behavior for `Foo`s, and a different behavior for everything else. That includes `Object`. Can you really do something usefully with an `Object` if there's something you want to do with a `Foo`? – Andy Turner Jan 06 '20 at 08:54
  • Answer is not, java generics are not c++ templates. c++ templates are far more complex than generics... – Jean-Baptiste Yunès Jan 06 '20 at 08:59
  • @AndyTurner, yes, exactly, I'm not sure you are familiar with C++ template deduction, the code above is pretty common in C++, let me edit it with more details for Java experts. –  Jan 06 '20 at 09:00
  • @reavenisadesk I am quite familiar with code like that. I am saying: the solution depends on the problem you're trying to solve. – Andy Turner Jan 06 '20 at 09:01
  • 1
    picking features of different languages and comparing them in isolation is futile. Templates and generics are different solutions to different problems – 463035818_is_not_an_ai Jan 06 '20 at 09:46

3 Answers3

2

I find that philosophical questions about generics types like Foo<T> tend to be a bit vague; let's reframe this in terms of something familiar, a simplified List interface:

interface List<T> {
  int size();
  T get(int i);
  boolean add(T element);
}

A List<T> is a list which contains instances of T. You are saying you want the list to have different behavior when, say, T is Integer.

It's tempting to read List<T> as "a List of Ts"; but it's not. It's a List, just a plain old List, containing Objects. The <T> is an instruction to the compiler:

  • Whenever I add something to this list, make sure the Object I try to add can be cast to a T.
  • Whenever I get something from this list, cast it to a T before I do anything with it.

So, code like this:

List<Integer> list = ...
Integer i = list.get(0);

is desugared by the compiler to:

List list = ...
Integer i = (Integer) list.get(0);

And code like this:

list.add(anInteger);  // Fine.

Object object = ...
list.add(object);  // Oi!

is checked by the compiler, which says "fine" in the first case, and "oi! You can't add an Object to this list!", and compilation fails.

That's really all generics is: it's a way of eliding casts, and getting the compiler to sanity check your code.

This used to be done by hand in pre-generics code: you had to keep track of what type of element should be in the list, and make sure you only add/get things of that type out.

For simple programs, that's feasible; but it quickly becomes too much for one person (or, more pertinently, a team of people) to hold in their heads. And it's - literally - unnecessary cognitive burden, if the compiler can do that checking for you.

So, you can't specialize generics, because it's simply removing a bunch of casts. If you can't do it with a cast, you can't do it with generics.


But you can do specialized things with generics; you just have to think about them in a different way.

For example, consider the Consumer interface:

interface Consumer<T> {
  void accept(T t);
}

This allows you to "do" things with instances of a particular type. You could have a Consumer for Integers, and a Consumer for Objects:

AtomicInteger atInt = new AtomicInteger();
Consumer<Integer> intConsumer = anInt::incrementAndGet;

Consumer<Object> objConsumer = System.out::println;

Now, you can have a generic method which takes a generic List and a Consumer of the same type (*):

<T> void doSomething(List<T> list, Consumer<T> consumer) {
  for (int i = 0; i < list.size(); ++i) {
    consumer.accept(list.get(i));
  }
}

So:

List<Integer> listOfInt = ...
doSomething(listOfInt, intConsumer);

List<Object> listOfObj = ...
doSomething(listOfObj, objConsumer);

The point here is that while generics here are simply removing casts, it's also checking that the T is the same for list and consumer. You can't write

doSomething(listOfObj, intConsumer);  // Oi!

So, the specialization comes from outside the definition of doSomething.


(*) Actually, it's better to define this as Consumer<? super T>; see What is PECS for an explanation.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
  • thanks Andy, can I think in this way: all the different design philosophies between Java and C++ starts from the type checking, Java just stop the compilation if type checking fails, and C++ tries to continue if possible? –  Jan 06 '20 at 09:46
1

Java generics and C++ template, while they look similar and attempt to solve similar problems, are very different.

In C++, templates work by creating a new class for each unique type it is unsed for (hence why it's called "templates"). So if you use vector<foo> and vector<bar>, for instance, the compiler will generate different machine code for the two classes (even though they share the same template). It's clear to see then that there's no fundamental obstacle to having template overloading, so that some template arguments will generate classes that look different from others, based on certain conditions (such as inheritance).

In Java, on the other hand, generics work by type erasure. A generic class in Java is only generated once, but the generic types are replaced by more general types, and the type checking is done at build time instead.

So this code:

public class Foo<T extends Bar> {
    public static T do_something(T value) {
        // ...
    }
}

public class Bar { }

public class Baz extends Bar { }

public class UseFoo {
    void use_foo() {
        Baz baz = new Baz();
        baz = Foo.do_something(baz);
    }
}

Essentially becomes this code internally when it's being built:

// note how the generic type is gone and replaced by `Bar`
public class Foo {
    public static Bar do_something(Bar value) {
        // ...
    }
}

public class Bar { }

public class Baz extends Bar { }

public class UseFoo {
    void use_foo() {
        Baz baz = new Baz();
        // note the cast here, since we know (because of generics)
        // that this type must be of type `Baz` when given the input of type `Baz`
        baz = (Baz) Foo.do_something(baz);
    }
}

So because Java has generics based on type erasure, there will only ever be one implementation of the Foo class, so it's not possible to override with different behavior based on the type arguments.

Frxstrem
  • 38,761
  • 9
  • 79
  • 119
  • Hi @Frxstrem, does that mean if I were a java compiler writer, I will replace the `T` with `Bar` in syntax like , and replace `T` with class `Object` in syntax like ``, just that simple? And if in runtime the parameter do not fit, exception throws? –  Jan 06 '20 at 09:22
  • @reavenisadesk Essentially, yes (at least the first part). Though I'm not quite familar with how the Java compiler actually works, so they probably do smarter things, but it boils down to the same idea—type erasure—either way. – Frxstrem Jan 06 '20 at 09:27
  • 1
    @reavenisadesk "And if in runtime the parameter do not fit" No: generics are purely compile-time. – Andy Turner Jan 06 '20 at 09:28
  • 2
    Exactly. Templates create new classes. Generics restricts existing classes. – user207421 Jan 06 '20 at 09:38
0

I think the best way to do this is to have a separate class for each case with a basic interface

interface FooInterface {}
class FooGeneric<T> implements FooInterface {}
class FooForFoo<T extends Foo> implements FooInterface {}

And use a Factory to return the correct type

public FooInterface getFoo(Object o) {
   // you may want to cast the object to the correct type
   return (o instanceof Foo) ? new FooForFoo<>() : new FooGeneric<>() ;
}
A.Oubidar
  • 391
  • 2
  • 10