开发者

Java Generics Class Parameter Type Inference

Given the interface:

public interface BasedOnOther<T, U extends BasedList<T>> {

    public T getOther();

    public void staticStatisfied(final U list);

}

The BasedOnOther<T, U extends BasedList<T>> looks very ugly in my use-cases. It is because the T type parameter is already defined in the BasedList<T> part, so the "uglyness" comes from that T needs to be typed twice.

Problem: is it possible to let the Java compiler infer the generic T type from BasedList<T> in a generic class/interface definition?

Ultimately, I'd like to use the interface like:

class X implements BasedOnOther<Y> {
    public SomeType getOth开发者_StackOverflow中文版er() { ... }
    public void staticStatisfied(final Y list) { ... }
} // Does not compile, due to invalid parameter count.

Where Y extends BasedList<SomeType>.

Instead:

class X implements BasedOnOther<SomeType, Y> {
    public SomeType getOther() { ... }
    public void staticStatisfied(final Y list) { ... }
}

Where Y extends BasedList<SomeType>.

Update: ColinD suggested

public interface BasedOnOther<T> {
    public T getOther();
    public void staticSatisfied(BasedList<T> list);
}

It is impossible to create an implementation such as:

public class X implements BasedOnOther<SomeType> {
    public SomeType getOther() { ... }
    public void staticStatisfied(MemoryModel list);
} // Does not compile, as it does not implement the interface.

Where MemoryModel extends BasedList<SomeType>, which is needed (as it provides other methods).


It looks as if you don't actually need the type parameter U extends BasedList<T>, if you don't actually need to do anything in the class that requires some specific subclass/implementation of BasedList<T>. The interface could just be:

public interface BasedOnOther<T> {
  public T getOther();
  public void staticSatisfied(BasedList<T> list);
}

Edit: Based on your update, I don't think there's any way you can do this. I think you'll have to either just go with your original declaration or make some intermediate type that specifies T, like:

public interface BasedOnSomeType<U extends BasedList<SomeType>>
         extends BasedOnOther<SomeType, U>
{
}

public class X implements BasedOnSomeType<MemoryModel> { ... }

That seems like kind of a waste though, and I don't really think the original declaration looks that bad.


What about this?

public interface BasedOnOther<T> {
    public T getOther();
    public <U extends BasedList<T>> void staticStatisfied(final U list);
}


ColinD is almost correct. What you probably want is this:

public interface BasedOnOther<T> {
  public T getOther();
  public void staticSatisfied(BasedList<? extends T> list);
}

That's because method arguments are covariant but the generics are invariant. Look at this example:

public test() {
  Number n1;
  Integer n2; //Integer extends Number.  Integer is a more-specific type of Number.

  n1 = n2; //no problem
  n2 = n1; //Type mismatch because the cast might fail.

  List<Number> l1;
  List<Integer> l2;
  List<? extends Number> l3;

  l1 = l3; //No problem.
  l1 = l2; //Type mismatch because the cast might fail.
}

Why:

Trying to put an Integer where a Number belongs is covariance and it's usually correct for function arguments.

Trying to put a Number where an Integer belongs is the opposite, contravariance, and it's usually correct for function return values. For example, if you defined a function to return a Number, it could return an Integer. If you defined it to return an Integer, however, you couldn't return a Number because that might be a floating-point, for example.

When you're dealing with generics, the compiler can't tell if the generic parameter (in your case, T) is going to be covariant or contravariant. In your code, for example, T is once a return value and once part of an argument. For that reason, generic parameters are invariant by default.

If you want covariance, use <? extends T>. For contravariance, use <? super T>. As a rule of thumb, you probably always want to specify the covariance/contravariance on all public functions. For private functions it doesn't matter as much because you usually already know the types.

This isn't specific to java, other object-oreiented languages have similar issue.


I recently had a very similar problem.

I would suggest, if you are do not need to specifically refer to MemoryModel, i.e. if U extends BasedList<T> is enough, then I would definitely do what Pepe answered.

However, if you must type check for at least two methods that both methods must use MemoryModel specifically and the type inferencing in Pepe's answer is not enough, then the only way to make the use of the clumsy/verbose parameterized constructor more simplistic, is to exploit the generic method parameter inferencing. You would need to create generic static factory methods for each constructor, where the factory methods do the type inferencing (constructors can't type inference in Java).

How to do this is covered in

Effective Java, by Joshua Block; Item 27: Favor generic methods

I also explained this and quoted the solution (with code) here.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜