繁体   English   中英

Java泛型类参数类型推断

[英]Java Generics Class Parameter Type Inference

鉴于界面:

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

    public T getOther();

    public void staticStatisfied(final U list);

}

在我的用例中BasedOnOther<T, U extends BasedList<T>>看起来非常难看。 这是因为T类型参数已经在BasedList<T>部分中定义,因此“丑陋”来自T需要键入两次。

问题 :是否可以让Java编译器在泛型类/接口定义中从BasedList<T>推断出泛型T类型?

最终,我想使用如下界面:

class X implements BasedOnOther<Y> {
    public SomeType getOther() { ... }
    public void staticStatisfied(final Y list) { ... }
} // Does not compile, due to invalid parameter count.

其中Y extends BasedList<SomeType>

代替:

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

其中Y extends BasedList<SomeType>

更新 :ColinD建议

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

创建一个实现是不可能的,例如:

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

MemoryModel extends BasedList<SomeType> ,这是必需的(因为它提供了其他方法)。

看起来好像你实际上并不需要类型参数U extends BasedList<T> ,如果你实际上不需要在类中做任何事情,需要一些特定的子类/实现BasedList<T> 界面可能只是:

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

编辑 :根据您的更新,我认为您无法做到这一点。 我想你要么只需要使用原始声明,要么制作一些指定T中间类型,例如:

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

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

这看起来像是一种浪费,我并不认为原始声明看起来那么糟糕。

那这个呢?

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

ColinD几乎是正确的。 你可能想要的是这个:

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

那是因为方法参数是协变的,但泛型是不变的 看看这个例子:

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.
}

为什么:

试图将一个Integer放在一个Number所属的是协方差,它通常对函数参数是正确的。

试图将一个Number放在一个Integer所属的是相反的,逆变量,它通常对函数返回值是正确的。 例如,如果您定义了一个返回Number的函数,它可能会返回一个Integer。 但是,如果您将其定义为返回一个Integer,则无法返回Number,因为这可能是一个浮点数。

当你处理泛型时,编译器无法判断泛型参数(在你的情况下,T)是否是协变的或逆变的。 例如,在你的代码中,T曾经是一个返回值,曾经是参数的一部分。 因此,默认情况下,通用参数是不变的。

如果你想要协方差,请使用<? extends T> <? extends T> 对于逆转,使用<? super T> <? super T> 根据经验,您可能总是希望在所有公共函数上指定协方差/逆变。 对于私有函数而言,它并不重要,因为您通常已经知道了类型。

这不是特定于java,其他面向对象的语言也有类似的问题。

我最近有一个非常类似的问题。

我建议,如果你不需要专门引用MemoryModel ,即如果U extends BasedList<T>就足够了,那么我肯定会做Pepe回答的问题。

但是,如果你必须键入check至少两个方法,两个方法必须专门使用MemoryModel并且Pepe的答案中的类型推理是不够的,那么使笨拙/详细参数化构造函数的使用更加简单化的唯一方法是利用泛型方法参数推理。 您需要为每个构造函数创建通用的静态工厂方法,其中工厂方法执行类型推理(构造函数不能在Java中键入推理)。

如何做到这一点涵盖在内

有效的Java,Joshua Block; 第27项:偏好通用方法

我也解释了这一点,并在这里引用了解决方案(带代码)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM