当我们在泛型类中需要一个额外的参数时,钻石操作符会发生什么变化?

huangapple go评论55阅读模式
英文:

What does diamond operator do when we need one more parameter in generic class?

问题

class GenericBase<T> {
    private T baseVariable;
    
    public T get() {
        return baseVariable;
    }
    
    public void set(T t){
        baseVariable = t;
    }
}

class GenericSubTwo<T, V> extends GenericBase<T> {
    private V subVariable2;
    
    public V get2(){
        return subVariable2;
    }
}

public class TestClass {
    public static void main(String[] args){
        GenericBase<Integer> sub2 = new GenericSubTwo<>();
    }
}

在这种情况下,钻石操作符实际上创建了一个新的 GenericSubTwo<Integer, Integer> 对象。我不太理解发生了什么,因为在 GenericSubTwo 中我需要两个参数...

英文:
class GenericBase&lt;T&gt; {
	private T baseVariable;
	
	public T get() {
		return baseVariable;
	}
	
	public void set(T t){
		baseVariable = t;
	}
}
class GenericSubTwo&lt;T, V&gt; extends GenericBase&lt;T&gt;{

	private V subVariable2;
	
	public V get2(){
		return subVariable2;
	}
}


public TestClass{

public static void main(String[] args){
GenericBase&lt;Integer&gt; sub2 = new GenericSubTwo&lt;&gt;();
}
}

Is it true that diamond operator in this situation actually make new GenericSubTwo<Integer, Integer>();
or maybe something else.. I do not understand what is going on, because in GenericSubTwo I need two parameters..

答案1

得分: 4

以下是您要翻译的内容:

无论如何,这实际上并不重要,因为您无论如何都看不到 V 是什么。

推断 VObject,因为它是对 V 的限制。

更新

实际上,似乎推断 V?,因为它可以转换为任何类型。

GenericBase<Integer> sub2 = new GenericSubTwo<>();

// 有效的转换:T 必须是 Integer,但 V 可以是任何类型
GenericSubTwo<Integer, String> x = (GenericSubTwo<Integer, String>) sub2;
英文:

It really doesn't matter, because you cannot see what V is anyway.

V inferred to be Object, since that is the bound on V.

UPDATE

Actually, it seems V is inferred to be ?, because it can be cast to anything.

GenericBase&lt;Integer&gt; sub2 = new GenericSubTwo&lt;&gt;();

// Valid cast: T must be Integer, but V can be anything
GenericSubTwo&lt;Integer, String&gt; x = (GenericSubTwo&lt;Integer, String&gt;) sub2;

答案2

得分: 1

> "在这种情况下使用钻石操作符是否真的会创建一个新的GenericSubTwo<Integer,Integer>();"

不,那是不正确的。

> "或者可能是其他什么"

是的,其他的东西。那个「其他的东西」是 Object。这是因为类型擦除

因此,你实际上会有一个参数化的类型,就像这样<strike>GenericSubTwo&lt;Object,Integer&gt;()</strike>,就好像你这样做 GenericBase&lt;Integer&gt; sub2 = new GenericSubTwo&lt;Integer,Object&gt;()

英文:

> „Is it true that diamond operator in this situation actually make new GenericSubTwo<Integer, Integer>();

No. That's untrue.

> „or maybe something else

Yes. Something else. And that „something else“ is Object. That's because of Type Erasure.

So, the actual parameterized type you'd have would be <strike>GenericSubTwo&lt;Object, Integer&gt;()</strike> as if you did GenericBase&lt;Integer&gt; sub2 = new GenericSubTwo&lt;Integer, Object&gt;().

答案3

得分: 0

这有点像是一个哲学问题,因为在编译后的字节码中,new GenericSubTwo&lt;Integer, Object&gt;()new GenericSubTwo&lt;String, String&gt;()new GenericSubTwo() 之间没有区别。因此,如果你说它使用了特定的类型参数,但它并不影响结果,那它真的存在吗?

请记住,在运行时,对象并不知道它们的泛型类型参数,因此在运行时创建对象时,你不需要知道泛型类型参数。在 new 中提供的泛型类型参数仅在编译时用于类型检查的目的(例如,检查与参数类型的一致性,如果有的话,并检查返回类型是否有效)。在类型检查之后,这些类型参数就不再重要了,因为它们不会被编译成字节码。在这种特殊情况下,由于构造函数没有参数,并且返回值赋给了 GenericBase&lt;Integer&gt;,其中 GenericSubTwo&lt;T, V&gt;V 没有被使用,因此对 V 没有任何约束。

因此,我们可以思考编译器对带有 diamond 表达式的对象创建表达式的不同思考方式:

  • 编译器可以尝试“推断”每个类型参数的确切类型参数,并检查该选择是否适用于参数和返回上下文;或者
  • 编译器可以尝试证明存在至少一种类型参数的选择使得它能够编译并使得类型检查工作,只要能够证明至少存在一种选择,它就不需要选择其中的哪一个,因为在生成字节码时实际的类型参数不会被使用。
英文:

This is kind of a philosophical question, because in the compiled bytecode, there is no difference between new GenericSubTwo&lt;Integer, Object&gt;(), new GenericSubTwo&lt;String, String&gt;(), and new GenericSubTwo(). So if you say it uses a particular type argument, but it doesn't affect the result, does it really exist?

Remember that objects do not know their generic type arguments at runtime, so when creating an object at runtime, you do not need to know the generic type arguments. The generic type arguments provided in the new are only used at compile-time for type checking purposes (e.g. to check consistency with argument types, if any, and to check if that return type works). After the type checking, those type arguments don't matter anymore, as they are not emitted into the compiled bytecode. In this particular case, since the constructor has no parameters, and the return values is assigned to a GenericBase&lt;Integer&gt; where the V of GenericSubTwo&lt;T, V&gt; is not used, there are no constraints on what V can be.

So we can think of different ways the compiler thinks about an object creation expression with the diamond:

  • The compiler could try to "infer" some exact type argument for each of the type parameters, and check that that choice works with the arguments and return context; or
  • The compiler could try to prove that there exists at least one choice of type argument that would make it compile and make the type checking work, and as long as it can prove that there exists at least one, it does not need to choose which one of them it is, since the actual type argument is not used when emitting the bytecode.

huangapple
  • 本文由 发表于 2020年8月25日 21:10:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/63579589.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定