为什么以下代码会编译?该方法IElement.getX(String)
返回该类型IElement
或其子类的实例。类中的代码Main
调用该getX(String)
方法。编译器允许将返回值存储到类型的变量Integer
(显然不在的层次结构中IElement
)。
public interface IElement extends CharSequence {
<T extends IElement> T getX(String value);
}
public class Main {
public void example(IElement element) {
Integer x = element.getX("x");
}
}
IElement
即使在擦除类型之后, 返回类型也不应该仍然是的实例 吗?
该getX(String)
方法的字节码为:
public abstract <T extends IElement> T getX(java.lang.String);
flags: ACC_PUBLIC, ACC_ABSTRACT
Signature: #7 // <T::LIElement;>(Ljava/lang/String;)TT;
编辑:String
一致地替换为Integer
。
这实际上是合法的类型推断*。
我们可以将其简化为以下示例(Ideone):
interface Foo {
<F extends Foo> F bar();
public static void main(String[] args) {
Foo foo = null;
String baz = foo.bar();
}
}
String & Foo
因为Foo
是接口,所以允许编译器推断(无意义的,实际上是)交集类型。对于问题中的示例,Integer & IElement
可以推断。
这是荒谬的,因为转换是不可能的。我们自己不能做这样的演员:
// won't compile because Integer is final
Integer x = (Integer & IElement) element;
类型推断基本上适用于:
在算法结束时,将根据绑定集将每个变量 解析 为交集类型,如果有效,则将编译调用。
该过程从8.1.3开始:
当推理开始时,通常从类型参数声明和关联的推理变量的列表中生成绑定集。这样的绑定集构造如下。对于每个 l(1≤l≤p) :
P1, ..., Pp``α1, ..., αp
__
[…]
否则,对于在
TypeBound
中以T
分隔的每种类型,界限将出现在集合[…]中。&
__α l <: T[P1:=α1, ..., Pp:=αp]
因此,这意味着首先编译器以F <: Foo
(F
的子类型为Foo
)的边界开始。
转到18.5.2,将考虑返回目标类型:
如果调用是多边形表达式,则[…]
R
设为的返回类型m
,T
设为调用的目标类型,然后:
[…]
否则,约束公式将
‹R θ → T›
被简化并与[绑定集]合并。
约束公式‹R θ → T›
被简化为的另一个边界R θ <: T
,因此我们有F <: String
。
稍后根据18.4解决:
[…] 为每个定义一个候选实例:
Ti``αi
- 否则,在适当的上限处,。
αi``U1, ..., Uk``Ti = glb(U1, ..., Uk)
边界与当前边界集合并。
α1 = T1, ..., αn = Tn
回想一下我们的范围是F <: Foo, F <: String
。glb(String, Foo)
定义为String & Foo
。这显然是glb的合法类型,仅要求:
如果对于任何两个 类 ( 不是interfaces )和,都不是的子类,反之亦然,则是编译时错误。
Vi``Vj``Vi``Vj
最后:
如果解析通过实例化实例变量成功完成,则将其替换。然后:
T1, ..., Tp``α1, ..., αp``θ'``[P1:=T1, ..., Pp:=Tp]
- 如果不加以控制的转换是不必要的方法可应用于,然后的调用类型
m
是通过将获得θ'
到的类型m
。
因此,String & Foo
以的类型调用该方法F
。我们当然可以将其分配给a String
,因此不可能将a转换Foo
为a String
。
String
/ Integer
是最后一堂课的事实显然未被考虑。
*注意:类型 清除 与问题完全无关。
另外,尽管这也可以在Java 7上编译,但是我认为可以不必担心那里的规范是合理的。Java 7的类型推断本质上是Java
8的较不复杂的版本。出于类似原因进行编译。
作为附录,虽然很奇怪,但这可能永远不会引起尚未出现的问题。编写其返回类型仅从返回目标推断出的泛型方法几乎是没有用的,因为只能null
从此类方法返回而无需强制转换。
例如,假设我们有一些映射类似物,它存储特定接口的子类型:
interface FooImplMap {
void put(String key, Foo value);
<F extends Foo> F get(String key);
}
class Bar implements Foo {}
class Biz implements Foo {}
进行如下所示的错误已经完全有效:
FooImplMap m = ...;
m.put("b", new Bar());
Biz b = m.get("b"); // casting Bar to Biz
因此,我们也 可以 做到这一事实Integer i = m.get("b");
并不是错误的 新
可能性。如果我们正在对这样的代码进行编程,那么一开始它就已经很不完善了。
通常,仅在没有理由绑定目标参数的情况下,才应仅从目标类型中推断出类型参数,例如Collections.emptyList()
和Optional.empty()
:
private static final Optional<?> EMPTY = new Optional<>();
public static<T> Optional<T> empty() {
@SuppressWarnings("unchecked")
Optional<T> t = (Optional<T>) EMPTY;
return t;
}
可以,因为Optional.empty()
既不能产生也不能消耗T
。
Java中是否有一种方法可以通过一个方法的声明返回不同的类型? 我希望此方法返回一个对象,并在函数调用时将其转换为正确的类型。这就是我的想法,但它不是这样工作的。我是否需要某种通用返回类型来执行此操作?解决这个问题的最佳方法是什么?
下面对getHighest()和getLowest()的调用返回Comparable类型的对象,而不是T类型的对象,这正是我们想要的。为什么,我该如何改进这段代码,使这些调用返回T(这样T的字段和方法就可用了)? 下一行生成编译器错误: 错误:找不到符号符号:方法getName()位置:接口java.lang.Comparable 我想employee.getHighest()返回一个员工(而不仅
ArrayList的get()方法为什么可以直接将Object数组返回成泛型里的T? 今天阅读ArrayList源码时,为什么可以直接在返回类型为泛型的方法里直接返回Object数组,而我自己定义的类里却需要强转? 以下是ArrayList源码 而我的代码却无法编译
我正在阅读《有效Java》第5章关于泛型的内容,特别是关于偏好泛型方法的内容。我注意到,有时返回类型之前的方法声明中的类型参数(尖括号之间)会被省略。类似的案例有很多,但例如第二版第135页: 另一方面,我看到了类似的泛型方法与声明 第一个是错别字吗?如果不是,我什么时候可以省略声明中的括号? 谢啦
问题内容: 背景 我曾经写过这种方法: 应该这样称呼它: 这很好用(尽管我在研究当前容易出错的问题时在这里的答案中已经看到)。 目前的情况 无论如何,现在我正在编写以下代码(在扩展javax.servlet.jsp.tagext.TagSupport的类中): 目的是可以这样称呼: 我的评估方法中的代码显然不起作用。的第二个参数应该是Class对象。这导致我: 我的问题 如何获得通用(返回)类型的
在研究泛型时,我注意到泛型方法和泛型类型(类或接口)在类型引入语法上的一个差异使我感到困惑。 泛型方法的语法为 文件上说 为了彼此保持一致,我希望方法语法为 ,或者类型语法(for class)为,但事实显然并非如此。 为什么一个要介绍在前,另一个要介绍在后? 我主要以的形式使用泛型,并认为可能看起来很奇怪,但这是一个主观的参数,此外对于方法也是这样。您可以调用,类似于 在寻找技术解释时,我想在指