Java 泛型:有关使用泛型方法进行类型捕获和生成推理的问题

发布于 2024-12-07 01:45:56 字数 1196 浏览 0 评论 0原文

这是我上一个问题的后续问题,但由于上一个线程很长,我决定开始另一个与几乎相同主题相关的线程。

public class GenericMethodInference {

static <T> void test1(T t1, T t2) {}
static <T> void test3(T t1, List <T> t2) {}  
static <T> void test4(List <T> t1, List <T> t2) {}

public static void main(String [] args) {

    List <Object> c = new LinkedList<Object>();
    List <? extends Object> d = new ArrayList<Integer>();
    List e = new ArrayList<Integer>();

    test1("Hello", new Integer(1)); // ok clause (1)
    GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2)
    test3("Hello", c); // ok clause (3)
    test4(d,d) // clause (4) Error due to different type capture generated

}

注意:如果将光标移到每个子句上,您将看到正在生成并显示在 Eclipse 上的推理

:子句 (1) 将产生 测试1
b.子句 (2) 将准确生成实际类型参数中定义的内容
c.子句(3)将产生test3 <对象,列表<对象>>

问题:

  1. 为什么子句 (1) 没有产生 ?由于<对象>工作原理如第(2)条所示,为什么而是生产?
  2. 为什么子句 (3) 产生 而不是 ?
  3. 既然第(4)条使用了相同的变量,为什么即使使用的参数是相同的变量d,也会生成2个不同类型的捕获?

This is a follow-up to my previous question but since the previous thread was a long one, i decided to start another thread pertaining to the almost same topic.

public class GenericMethodInference {

static <T> void test1(T t1, T t2) {}
static <T> void test3(T t1, List <T> t2) {}  
static <T> void test4(List <T> t1, List <T> t2) {}

public static void main(String [] args) {

    List <Object> c = new LinkedList<Object>();
    List <? extends Object> d = new ArrayList<Integer>();
    List e = new ArrayList<Integer>();

    test1("Hello", new Integer(1)); // ok clause (1)
    GenericMethodInference.<Object>test1("Hello", new Integer(1)); // ok clause (2)
    test3("Hello", c); // ok clause (3)
    test4(d,d) // clause (4) Error due to different type capture generated

}

Note: If you move your cursor over each clause, you will see the inference being generated and displayed on Eclipse:

a. Clause (1) will produce <? extends Object> test1 <? extends Object, ? extends Object>
b. Clause (2) will produce exactly what's defined in the actual type parameter
c. Clause (3) will produce <Object> test3 <Object, List <Object>>

Questions:

  1. Why clause (1) didn't produce <Object>? Since <Object> works as shown in clause (2), why <? extends Object> being produce instead?
  2. why clause (3) produce <Object> instead of <? extends Object>?
  3. Since clause (4) uses the same variable, why 2 different type capture generated eventhough the parameter used is of the same variable d?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

雨落□心尘 2024-12-14 01:45:56

为什么子句 (1) 没有生成 ?由于<对象>工作原理如第(2)条所示,为什么而是生产吗?

这是三个问题中最好的一个。我的想法是,编译器/Eclipse 不想假设 Object 必然是在 String之间推断出的 T 类型>整数,所以它是安全的。正如 @bringer128 指出的,StringInteger 也都实现了SerializedComparable - 因此这些类型也是该方法的推断类型的候选类型。

值得注意的是,以下代码给出了编译器错误“非法类型开始”:

GenericMethodInference.<? extends Object>test1("Hello", new Integer(1));

这是因为将通配符指定为方法的类型参数是无效的。因此,您在工具提示中看到的事实与编译器/Eclipse 报告此信息的功能的微妙有关 - 它仅确定 T 在其范围内,而不是它是什么。

请记住,Java 的泛型实现只是为了程序员的方便/理智。一旦编译成字节码,类型擦除将被消除T 的任何概念。因此,在检查时,编译器只需要确保可以推断出a有效的T,但不一定推断出它是什么。


为什么子句(3)产生而不是 ?

因为在这种情况下,在需要 List 的地方传入 List的事实告诉编译器 T正是对象


既然第(4)条使用了同一个变量,为什么即使使用的参数是同一个变量d,也会生成2个不同类型的捕获?

编译器假设 d 实际上引用同一个对象是不安全的,即使在评估参数之间也是如此。例如:

test4(d,(d = new ArrayList<String>()));

在本例中,List 将传递到第一个参数,List 将传递到第二个参数 - 均来自 d。由于这种情况是可能的,因此编译器更容易安全行事。

Why clause (1) didn't produce <Object>? Since <Object> works as shown in clause (2), why <? extends Object> being produce instead?

This is the best question out of the three. My thinking is that the compiler/Eclipse doesn't want to assume that Object is necessarily the type T that is inferred between String and Integer, so it plays it safe. As @bringer128 pointed out, String and Integer also both implement Serializable and Comparable - so these types are also candidates for the inferred type of the method.

It's worth noting that the following code gives the compiler error "illegal start of type":

GenericMethodInference.<? extends Object>test1("Hello", new Integer(1));

This is because it's invalid to specify a wildcard as a method's type parameter. So the fact you're seeing that in the tooltip has to do with a subtlety of the compiler's/Eclipse's facility to report this information - it has determined only that T is within its bounds, not what it is.

Remember that Java's implementation of generics is solely for the convenience/sanity of programmers. Once compiled into bytecode, type erasure will have gotten rid of any notion of T. So in its checking, the compiler only needs to ensure that a valid T can be inferred, but not necessarily what it is.


why clause (3) produce <Object> instead of <? extends Object>?

Because in this case, the fact that a List<Object> is passed in where a List<T> is expected tells the compiler that T is exactly Object.


Since clause (4) uses the same variable, why 2 different type capture generated eventhough the parameter used is of the same variable d?

It isn't safe for the compiler to assume that d actually refers to the same object, even between evaluating parameters. For example:

test4(d,(d = new ArrayList<String>()));

In this case, a List<Integer> would be passed into the first parameter, and an List<String> into the second - both from d. Since this scenario is possible, it's easier for the compiler to play it safe.

念三年u 2024-12-14 01:45:56

test1() 的情况实际上是相当险恶的。参见 JLS3 15.12.2.7。

我们不应该知道类型推断的细节——在大多数情况下,直觉与算法是一致的。可惜情况并非总是如此,就像看似微不足道的 test1() 示例一样。

我们的约束是 T :> String 和 T :> Integer (“:>”表示超类型)

这导致 T=lub(String,Integer)lub 表示“最小上限”。

由于 String <: ComparableInteger <: Comparable,这导致 lci({Comparable, Comparable;}),这会产生 Comparable,即Compable

最后,我们有 T = Serialized &兼容,一个自引用定义! Spec 称之为“无限类型”:

上述过程有可能产生无限类型。这是允许的,Java 编译器必须识别这种情况并使用循环数据结构适当地表示它们。

让我们看看 javac 是如何表示它的: (javac 7)

static <T> T test1(T t1, T t2) {}

public static void main(String[] args)
{
    Void x = test1("Hello", new Integer(1)); 
}

error: incompatible types
required: Void
found:    INT#1
where INT#1,INT#2 are intersection types:
INT#1 extends Object,Serializable,Comparable<? extends INT#2>
INT#2 extends Object,Serializable,Comparable<?>

这似乎不对;它并不是真正的递归;看来 javac 检测到 lub() 中的递归并放弃,导致不太具体的类型 Comparable

The test1() case is actually quite sinister. see JLS3 15.12.2.7.

We are not supposed to know the details of type inference - in most cases intuition coincides with the algorithm. Alas that's not always the case, as in the seemingly trivial test1() example.

The constraints we have is T :> String and T :> Integer ( ":>" means super type)

This leads to T=lub(String,Integer), lub means "least upper bound".

Since String <: Comparable<String> and Integer <: Comparable<Integer>, this leads to lci({Comparable<String>, Comparable<Integer>}), which yields Comparable<? extends lub(String,Integer)>, i.e. Compable<? extends T>

In the end, we have T = Serializable & Compable<? extends T>, a self referenced definition! Spec calls it "infinite type":

It is possible that the process above yields an infinite type. This is permissible, and Java compilers must recognize such situations and represent them appropriately using cyclic data structures.

Let's find out how javac represents it: (javac 7)

static <T> T test1(T t1, T t2) {}

public static void main(String[] args)
{
    Void x = test1("Hello", new Integer(1)); 
}

error: incompatible types
required: Void
found:    INT#1
where INT#1,INT#2 are intersection types:
INT#1 extends Object,Serializable,Comparable<? extends INT#2>
INT#2 extends Object,Serializable,Comparable<?>

That doesn't seems right; it's not really recursive; it appears that javac detects recursion in lub() and gives up, resulting a less specific type Comparable<?>

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文