强制Java泛型参数具有相同的类型

Evg*_*eny 12 java generics

如何在没有错误的情况下实现类似的功能?

class A<K> {
   void f(K x) {}
}

void foo(A<? extends X> a, X x) {
    a.f(x); // AN error: The method f(capture#1-of ? extends X) in the 
            // type A<capture#1-of ? extends X> is not applicable for the 
            // arguments (X)
}
Run Code Online (Sandbox Code Playgroud)

我知道它发生是因为'a'可以是A <"non-X">的一个实例,所以它的'f'不能接受X的一个实例作为参数,但我怎么能强制参数是相同的类型?

这是更多的代码:

测试类:

class Test {
   <T> void foo(A<T> a, T x) {
   a.f(x); // now it works!
 }
}
Run Code Online (Sandbox Code Playgroud)

在某些课程中:

Container<X> container;
public void test() {
    X x = new X();
    new Test().foo(container.get(), x);
}
Run Code Online (Sandbox Code Playgroud)

这是容器类:

public class Container<K> {
    A<? extends K> get() {
    return new A<K>();
    }
}
Run Code Online (Sandbox Code Playgroud)

Bru*_*eis 18

您可以通过执行以下操作强制参数具有相同的类型:

// the first class, A<K>:
class A<K> {
  void f(K x) {}
}

// the second class, defining the method with generic type parameters
class Test {
  <T> void foo(A<T> a, T x) {
    a.f(x); // now it works!
  }
}

// a third class, that uses the above two:
class Main {
  public static void main(final String... args) {
    final Test test = new Test();
    final A<String> a = new A<>();
    test.foo(a, "bar");
  }
}
Run Code Online (Sandbox Code Playgroud)

它的作用是:该方法foo定义泛型类型参数,T并使用它来强制K类的类型参数A必须与x第二个参数的类型相匹配foo.

<T>如果你愿意,你甚至可以施加限制,如果它对你的问题有意义,例如<T extends Bar> void foo(A<T> a, T x) {...},或者super.如果Joni在问题中的评论中询问,X实际上是一个类型而不是类型参数,你会想要这个:你会使用<T extends X> void foo(...).


在显示更多代码后,问题就变得清晰了.

.get()容器的方法返回一个实例A<? extends K>.因此,.get()未完全指定从中获取的实例的类型参数.通常,返回这种未指定类型的设计并不是很好.有关使用Effective Java的作者Joshua Bloch以及Java中的许多API和功能的视频演示,请展示如何改进此API,请查看:http://www.youtube.com/watch?v = V1vQf4qyMXg&feature = youout. be&t = 22m.正好在25'36",Joshua Bloch说"不要试图在返回值上使用它们[通配符类型]",他稍后会解释.基本上,你不会通过使用它们获得更多的灵活性,只是制作API的用户很难处理它(你只是觉得这样做的效果......).

要解决,你可以简单地尝试改变的签名.get()A<K> get(),因此容器类是:

public class Container<K> {
  A<K> get() {
    return new A<K>();
  }
}
Run Code Online (Sandbox Code Playgroud)

既然你知道它get()正在返回一个实例A<K>,那么就没有理由使用旧的签名:它只会让你丢失你已经知道的信息!

如果这仍然不起作用,您的问题可能在其他地方,并且您需要显示更多代码......或者更好,还要问其他问题!:)


Mar*_*nik 6

记住PECS规则,并且考虑到使用X的方式,您应该指定为下限而不是上限:

void foo(A<? super X> a, X x)
Run Code Online (Sandbox Code Playgroud)

这样就不会产生编译器错误,并且您可以使用最常用的签名.