Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java circular Generics

Tags:

java

generics

I'm having a problem understanding Java generics and I've simplified to this example

class A<T extends B> {

    public void fun(T t) {

    }
}

class B {
    A a;

    public void event() {
        a.fun(this);
    }

}

The problem is that this generates a warning because A is defined inside B but A is already using it as a generic type.

My first instinct would be that my design is wrong, but in this case I can't change it. A is like a collection and B is like a node in the collection that users are supposed to override. Certain events can happen in B that require reporting back to the parent A.

But since A is defined generically with B, how do I avoid the compile warning inside B.event()

Thanks

like image 608
Mike Avatar asked Aug 10 '09 08:08

Mike


People also ask

Are templates more powerful than generics?

In C++ when you use a template the compiler will emit the template code again after replacing the generic parameter in it with the type you used. This is more powerful in several ways but can lead to bloated executables.

Are C++ templates and Java generics implemented in the same way?

A C++ template gets reproduced and re-compiled entirely whenever a template is instantiated with a new class. The main difference is that Java generics are encapsulated. The errors are flagged when they occur and not later when the corresponding classes are used/instantiated.

How do Java templates work?

Template Method is a behavioral design pattern that allows you to defines a skeleton of an algorithm in a base class and let subclasses override the steps without changing the overall algorithm's structure.


1 Answers

The problem is that you're using a raw type on this line:

A a;

You need to specify a type for A's type parameter (T).

You could do something like this:

A<B> a;

but then A might as well not be generic at all, if I'm understanding your statement of the problem. You probably want to do something like this:

class A<T> {
  public void fun(T t) {

  }
}

class B<T extends B<T>> {
  A<B<T>> a;
  public void event() {
    a.fun(this);
  }
}    

or even this:

class A<T extends B<? extends T>> {
  public void fun(T t) {

  }
}

class B<T extends B<T>> {
  A<? super B<T>> a;
  public void event() {
    a.fun(this);
  }
}

There are a couple of variations in-between these that are possibly useful as well. The latter example is the most generic (but obviously, also the most complicated).

The class A<T extends B<? extends T>> is ensuring that the type parameter to A is a B. Since B is itself generic, and has that cyclic type parameter, you end up needing to say B<? extends T> (simply saying T won't work here).

The class B<T extends B<T>> is as close as you can get to emulating a "self type" in Java. This lets B talk about the (almost) concrete subtype of itself. When subclassing B you'd say something like "class C extends <B<C>>". This is useful because now the type of C.a is actually A<? super B<C>>.

The ? super bit in the latter example is only useful if you plan on connecting a B with an A that isn't for exactly the same type of B. Thinking in concrete terms, suppose you had an A<Shape> and a Circle (which extends Shape which extends B). The super-wildcard lets you use them together. Without it you'd need an A<Circle> rather than an A<Shape> for your Circle.

like image 199
Laurence Gonsalves Avatar answered Nov 15 '22 08:11

Laurence Gonsalves