为什么这个复杂的泛型示例不是用Java7编译的(而是用Java8编译的),我如何解决这个问题?

为什么这个复杂的泛型示例不是用Java7编译的(而是用Java8编译的),我如何解决这个问题?,java,generics,java-8,java-7,Java,Generics,Java 8,Java 7,这是密码。注释中包含Java 7和8的错误和警告 可以更改takeCollection的签名。。。因此,调用方可以调用它并传递SubRoot/Foo/Bar集合,并针对Java 7和Java 8编译它,而不必通过重载在方法签名中专门引用SubRoot/Foo/Bar类型,其中可能有很多,一些在依赖项目中,对这个项目不可见?如果是,怎么做 package generics; import java.util.Collection; // ----------------------------

这是密码。注释中包含Java 7和8的错误和警告

可以更改takeCollection的签名。。。因此,调用方可以调用它并传递SubRoot/Foo/Bar集合,并针对Java 7和Java 8编译它,而不必通过重载在方法签名中专门引用SubRoot/Foo/Bar类型,其中可能有很多,一些在依赖项目中,对这个项目不可见?如果是,怎么做

package generics;

import java.util.Collection;

// -------------------------------------------------------------------------------
// Cannot change this (start)
//-------------------------------------------------------------------------------
interface Root<F extends Foo<F,B>, B extends Bar<F,B>> {}
class     Foo <F extends Foo<F,B>, B extends Bar<F,B>> implements Root<F,B> {}
class     Bar <F extends Foo<F,B>, B extends Bar<F,B>> implements Root<F,B> {}

interface SubRoot extends Root<SubFoo, SubBar> {}
class     SubFoo  extends Foo <SubFoo, SubBar> implements SubRoot {}
class     SubBar  extends Bar <SubFoo, SubBar> implements SubRoot {}
//-------------------------------------------------------------------------------
// End of cannot change block.
//-------------------------------------------------------------------------------

public class GenericsTest {

  <R extends Root<F,B>, F extends Foo<F,B>, B extends Bar<F,B>>
  void takeOne(R one) {}

  // The following method signature can be changed a bit, but it has to accept a collection
  // of Roots, or ANY of its subtypes.
  <R extends Root<F,B>, F extends Foo<F,B>, B extends Bar<F,B>, C extends Collection<R>>
  void takeCollection(C collection) {}

  // ------------------------------------------
  // Test/illustration code that compiles well
  // ------------------------------------------

  @SuppressWarnings("rawtypes") // Warnings understood
  void testOneRoot1(Root root, Foo foo, Bar bar) {
    takeOne(root);
    // Java 1.7 and 1.8 agree about the warning, as expected.
    //
    // generics\GenericsTest.java:35: warning: [unchecked] unchecked method invocation: method takeOne in class GenericsTest is applied to given types
    //    takeOne(root);
    //           ^
    //  required: R
    //  found: Root
    //  where R,F,B are type-variables:
    //    R extends Root<F,B> declared in method <R,F,B>takeOne(R)
    //    F extends Foo<F,B> declared in method <R,F,B>takeOne(R)
    //    B extends Bar<F,B> declared in method <R,F,B>takeOne(R)

    takeOne(foo);
    // Java 1.7 and 1.8 agree about the warning, as expected.
    //
    // generics\GenericsTest.java:48: warning: [unchecked] unchecked method invocation: method takeOne in class GenericsTest is applied to given types
    //    takeOne(foo);
    //           ^
    //  required: R
    //  found: Foo
    //  where R,F,B are type-variables:
    //    R extends Root<F,B> declared in method <R,F,B>takeOne(R)
    //    F extends Foo<F,B> declared in method <R,F,B>takeOne(R)
    //    B extends Bar<F,B> declared in method <R,F,B>takeOne(R)

    takeOne(bar);
    // Java 1.7 and 1.8 agree about the warning, as expected.
    //
    // generics\GenericsTest.java:61: warning: [unchecked] unchecked method invocation: method takeOne in class GenericsTest is applied to given types
    //    takeOne(bar);
    //           ^
    //  required: R
    //  found: Bar
    //  where R,F,B are type-variables:
    //    R extends Root<F,B> declared in method <R,F,B>takeOne(R)
    //    F extends Foo<F,B> declared in method <R,F,B>takeOne(R)
    //    B extends Bar<F,B> declared in method <R,F,B>takeOne(R)
  }

  <R extends Root<F,B>, F extends Foo<F,B>, B extends Bar<F,B>>
  void testOneRoot2(R root, F foo, B bar) {
    takeOne(root); // All fine
    takeOne(foo);  // All fine
    takeOne(bar);  // All fine
  }

  void testOneSubRoot(SubRoot subRoot, SubFoo subFoo, SubBar subBar) {
    takeOne(subRoot); // All fine
    takeOne(subFoo);  // All fine
    takeOne(subBar);  // All fine
  }

  void testRootCollectionRaw(@SuppressWarnings("rawtypes") Collection<? extends Root> collection) {
    takeCollection(collection);
    // Java 1.7 quiet (not expected). Java 1.8 produces a warning, as expected:
    //
    //  generics\GenericsTest.java:89: warning: [unchecked] unchecked method invocation: method takeCollection in class GenericsTest is applied to given types
    //      takeCollection(collection);
    //                    ^
    //    required: C
    //    found: Collection<CAP#1>
    //    where C,R,F,B are type-variables:
    //      C extends Collection<R> declared in method <R,F,B,C>takeCollection(C)
    //      R extends Root<F,B> declared in method <R,F,B,C>takeCollection(C)
    //      F extends Foo<F,B> declared in method <R,F,B,C>takeCollection(C)
    //      B extends Bar<F,B> declared in method <R,F,B,C>takeCollection(C)
    //    where CAP#1 is a fresh type-variable:
    //      CAP#1 extends Root from capture of ? extends Root
 }

  // --------------------------------------------
  // Test/illustration code that does NOT compile
  // --------------------------------------------

  <R extends Root<F,B>, F extends Foo<F,B>, B extends Bar<F,B>>
  void testRootCollection1(Collection<? extends R> collection) {
    takeCollection(collection);
    //  JDK 1.8 quiet (as expected). JDK 1.7 yields an error (not expected):
    //
    //  generics\GenericsTest.java:112: error: invalid inferred types for R#1,F#1,B#1; inferred type does not conform to declared bound(s)
    //      takeCollection(collection);
    //                    ^
    //      inferred: CAP#1
    //      bound(s): Root<CAP#2,CAP#3>
    //    where R#1,F#1,B#1,C,R#2,F#2,B#2 are type-variables:
    //      R#1 extends Root<F#1,B#1> declared in method <R#1,F#1,B#1,C>takeCollection(C)
    //      F#1 extends Foo<F#1,B#1> declared in method <R#1,F#1,B#1,C>takeCollection(C)
    //      B#1 extends Bar<F#1,B#1> declared in method <R#1,F#1,B#1,C>takeCollection(C)
    //      C extends Collection<R#1> declared in method <R#1,F#1,B#1,C>takeCollection(C)
    //      R#2 extends Root<F#2,B#2> declared in method <R#2,F#2,B#2>testRootCollection1(Collection<? extends R#2>)
    //      F#2 extends Foo<F#2,B#2> declared in method <R#2,F#2,B#2>testRootCollection1(Collection<? extends R#2>)
    //      B#2 extends Bar<F#2,B#2> declared in method <R#2,F#2,B#2>testRootCollection1(Collection<? extends R#2>)
    //    where CAP#1,CAP#2,CAP#3 are fresh type-variables:
    //      CAP#1 extends R#2 from capture of ? extends R#2
    //      CAP#2 extends Foo<CAP#2,CAP#3> from capture of ?
    //      CAP#3 extends Bar<CAP#2,CAP#3> from capture of ?
  }

  <R extends Root<F,B>, F extends Foo<F,B>, B extends Bar<F,B>>
  void testRootCollection2(Collection<R> collection) {
    takeCollection(collection);
    //  JDK 1.8 quiet (as expected). JDK 1.7 yields an error (not expected):
    //
    //  generics\GenericsTest.java:136: error: invalid inferred types for R#1,F#1,B#1; inferred type does not conform to declared bound(s)
    //      takeCollection(collection);
    //                    ^
    //      inferred: R#2
    //      bound(s): Root<CAP#1,CAP#2>
    //    where R#1,F#1,B#1,C,R#2,F#2,B#2 are type-variables:
    //      R#1 extends Root<F#1,B#1> declared in method <R#1,F#1,B#1,C>takeCollection(C)
    //      F#1 extends Foo<F#1,B#1> declared in method <R#1,F#1,B#1,C>takeCollection(C)
    //      B#1 extends Bar<F#1,B#1> declared in method <R#1,F#1,B#1,C>takeCollection(C)
    //      C extends Collection<R#1> declared in method <R#1,F#1,B#1,C>takeCollection(C)
    //      R#2 extends Root<F#2,B#2> declared in method <R#2,F#2,B#2>testRootCollection2(Collection<R#2>)
    //      F#2 extends Foo<F#2,B#2> declared in method <R#2,F#2,B#2>testRootCollection2(Collection<R#2>)
    //      B#2 extends Bar<F#2,B#2> declared in method <R#2,F#2,B#2>testRootCollection2(Collection<R#2>)
    //    where CAP#1,CAP#2 are fresh type-variables:
    //      CAP#1 extends Foo<CAP#1,CAP#2> from capture of ?
    //      CAP#2 extends Bar<CAP#1,CAP#2> from capture of ?
  }

  void testSubRootCollection1(Collection<SubRoot> collection) {
    takeCollection(collection);
    //  JDK 1.8 quiet (as expected). JDK 1.7 yields an error (not expected):
    //
    //  generics\GenericsTest.java:158: error: invalid inferred types for R,F,B; inferred type does not conform to declared bound(s)
    //      takeCollection(collection);
    //                    ^
    //      inferred: SubRoot
    //      bound(s): Root<CAP#1,CAP#2>
    //    where R,F,B,C are type-variables:
    //      R extends Root<F,B> declared in method <R,F,B,C>takeCollection(C)
    //      F extends Foo<F,B> declared in method <R,F,B,C>takeCollection(C)
    //      B extends Bar<F,B> declared in method <R,F,B,C>takeCollection(C)
    //      C extends Collection<R> declared in method <R,F,B,C>takeCollection(C)
    //    where CAP#1,CAP#2 are fresh type-variables:
    //      CAP#1 extends Foo<CAP#1,CAP#2> from capture of ?
    //      CAP#2 extends Bar<CAP#1,CAP#2> from capture of ?
  }

  void testSubRootCollection2(Collection<? extends SubRoot> collection) {
    takeCollection(collection);
    //  JDK 1.8 quiet. JDK 1.7 yields an error:
    //
    //  generics\GenericsTest.java:177: error: invalid inferred types for R,F,B; inferred type does not conform to declared bound(s)
    //      takeCollection(collection);
    //                    ^
    //      inferred: CAP#1
    //      bound(s): Root<CAP#2,CAP#3>
    //    where R,F,B,C are type-variables:
    //      R extends Root<F,B> declared in method <R,F,B,C>takeCollection(C)
    //      F extends Foo<F,B> declared in method <R,F,B,C>takeCollection(C)
    //      B extends Bar<F,B> declared in method <R,F,B,C>takeCollection(C)
    //      C extends Collection<R> declared in method <R,F,B,C>takeCollection(C)
    //    where CAP#1,CAP#2,CAP#3 are fresh type-variables:
    //      CAP#1 extends SubRoot from capture of ? extends SubRoot
    //      CAP#2 extends Foo<CAP#2,CAP#3> from capture of ?
    //      CAP#3 extends Bar<CAP#2,CAP#3> from capture of ?
  }
}

如果我将takeCollection的签名更改为:


但是,存在未检查/不安全操作警告,所有警告都与原始类型有关。

如果我将takeCollection的签名更改为:


但是,存在未检查/不安全操作警告,所有警告都与原始类型有关。

我将更改签名,如下所示:

void takeOne(Root<?,?> one) {}

void takeCollection(Collection<? extends Root<?,?>> collection) {}

void testOneRoot1(Root<?,?> root, Foo<?,?> foo, Bar<?,?> bar) {}

<R extends Root<F,B>, F extends Foo<F,B>, B extends Bar<F,B>>
void testOneRoot2(R root, F foo, B bar) {}

void testOneSubRoot(SubRoot subRoot, SubFoo subFoo, SubBar subBar) {}

@SuppressWarnings("unchecked")
void testRootCollectionRaw(@SuppressWarnings("rawtypes") Collection<? extends Root> collection) {
   takeCollection((Collection<? extends Root<?,?>>)collection);    
}      

void testRootCollection1(Collection<? extends Root<?,?>> collection) {}

void testRootCollection2(Collection<? extends Root<?,?>> collection) {}
您的代码应该使用Java 7和Java 8的此签名进行编译,而不会出现错误或警告。在代码中有几个地方,用通配符替换泛型类型参数是有意义的。例如 void takeneroot one{}几乎等同于void takeneroot one{},因为如何绑定F和B已经由Root的签名定义了。testRootCollectionRaw是一个特例,因为它以rawtype为上限。我宁愿在testRootCollectionRaw方法中使用未经检查的强制转换来解决这个问题,而不是通过调整takeCollection签名使其工作


您的原始代码没有为Java7编译的原因是Java7中的类型推断没有Java8中的高级。java 8将考虑表达式类型上的目标类型。通常,在可能的情况下,最好用通配符替换显式类型参数。实践这种方法的一个副作用是,与泛型方法中的显式类型参数相比,Java7更容易处理通配符

我将更改签名,如下所示:

void takeOne(Root<?,?> one) {}

void takeCollection(Collection<? extends Root<?,?>> collection) {}

void testOneRoot1(Root<?,?> root, Foo<?,?> foo, Bar<?,?> bar) {}

<R extends Root<F,B>, F extends Foo<F,B>, B extends Bar<F,B>>
void testOneRoot2(R root, F foo, B bar) {}

void testOneSubRoot(SubRoot subRoot, SubFoo subFoo, SubBar subBar) {}

@SuppressWarnings("unchecked")
void testRootCollectionRaw(@SuppressWarnings("rawtypes") Collection<? extends Root> collection) {
   takeCollection((Collection<? extends Root<?,?>>)collection);    
}      

void testRootCollection1(Collection<? extends Root<?,?>> collection) {}

void testRootCollection2(Collection<? extends Root<?,?>> collection) {}
您的代码应该使用Java 7和Java 8的此签名进行编译,而不会出现错误或警告。在代码中有几个地方,用通配符替换泛型类型参数是有意义的。例如 void takeneroot one{}几乎等同于void takeneroot one{},因为如何绑定F和B已经由Root的签名定义了。testRootCollectionRaw是一个特例,因为它以rawtype为上限。我宁愿在testRootCollectionRaw方法中使用未经检查的强制转换来解决这个问题,而不是通过调整takeCollection签名使其工作


您的原始代码没有为Java7编译的原因是Java7中的类型推断没有Java8中的高级。java 8将考虑表达式类型上的目标类型。通常,在可能的情况下,最好用通配符替换显式类型参数。实践这种方法的一个副作用是,与泛型方法中的显式类型参数相比,Java7更容易处理通配符

哎呀,这是一些看起来很吓人的仿制药。你能不能把它删掉,只问一个错误文本墙,而不是所有这些?我也认为你期待了很多…大多数错误都是一样的。我在这里添加它们是为了完整性,因为有些人抱怨我最初问他们时他们没有看到它们。我已经把问题降到了最低限度-最重要的是前27行,其余的只是用各种方式说明问题。用通配符类型替换代码中的所有rawtype,例如用Foo替换Foo将删除所有警告,至少在Java 8中是这样。这很好,我并不担心这些——我在评论中如预期的那样指出了它们。出乎意料的是Java7没有发出所有的警告。哎呀,这是一些看起来很可怕的泛型。你能不能把它删掉,只问一个错误文本墙,而不是所有这些?我也认为你期待了很多…大多数错误都是一样的。我在这里添加它们是为了完整性,因为有些人抱怨我最初问他们时他们没有看到它们。我已经把问题降到了最低限度-最重要的是前27行,其余的只是用各种方式说明问题。用通配符类型替换代码中的所有rawtype,例如用Foo替换Foo将删除所有警告,至少在Java 8中是这样。这很好,我并不担心这些——我在评论中如预期的那样指出了它们。出乎意料的是,Java7并没有发出所有的警告。这实际上是可行的!有人能解释为什么会有不同吗?这确实有效!听起来别那么惊讶。嗯,我试过很多东西,但不完全是那种变体。为什么这对编译器来说意味着与上面显示的不同?void acceptP不应该是ac吗
与无效接受不同?不确定。我可以想象这与编译器不喜欢它不能计算C的实际界限有关,因为它被传递给一个上限集合。虽然不是一种智力上令人满意的反应,但只要承认它是一种务实的反应就行了;将它添加到你的技巧曲目中,以便下次在泛型地狱中尝试。哦,我已经实用地应用了它:。。。。但我想知道为什么这是一个问题,这样我可以防止它,更好地处理这种情况。事实上,我不能确定我是否将问题转移到了其他地方,这是一个未知开发人员使用的库,将用于未知代码-我不想破坏他们的代码。这意味着,如果可能的话,我可能必须创建方法变量重载来覆盖所有情况。这实际上是可行的!有人能解释为什么会有不同吗?这确实有效!听起来别那么惊讶。嗯,我试过很多东西,但不完全是那种变体。为什么这对编译器来说意味着与上面显示的不同?void acceptP p不应该与void acceptQ相同吗?不确定。我可以想象这与编译器不喜欢它不能计算C的实际界限有关,因为它被传递给一个上限集合。虽然不是一种智力上令人满意的反应,但只要承认它是一种务实的反应就行了;将它添加到你的技巧曲目中,以便下次在泛型地狱中尝试。哦,我已经实用地应用了它:。。。。但我想知道为什么这是一个问题,这样我可以防止它,更好地处理这种情况。事实上,我不能确定我是否将问题转移到了其他地方,这是一个未知开发人员使用的库,将用于未知代码-我不想破坏他们的代码。这意味着如果可能的话,我可能必须创建方法变量重载来覆盖所有情况。testRootCollectionRaw只是一个例子,我并不真正关心原始类型。通配符并不完全相同,尽管.testRootCollectionRaw只是一个例子,我并不真正关心原始类型。不过,通配符并不完全相同。