- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
上下文
我正在从事一个严重依赖泛型类型的项目。其关键组件之一是所谓的 TypeToken
,它提供了一种在运行时表示泛型类型并对其应用一些实用函数的方法。为了避免 Java 的类型删除,我使用大括号表示法 ( {}
) 创建一个自动生成的子类,因为这使得类型可具体化。
什么 TypeToken
基本上是
这是 TypeToken
的强简化版本这比原始实现更宽松。但是,我正在使用这种方法,因此我可以确保真正的问题不在于这些实用程序函数之一。
public class TypeToken<T> {
private final Type type;
private final Class<T> rawType;
private final int hashCode;
/* ==== Constructor ==== */
@SuppressWarnings("unchecked")
protected TypeToken() {
ParameterizedType paramType = (ParameterizedType) this.getClass().getGenericSuperclass();
this.type = paramType.getActualTypeArguments()[0];
// ...
}
TypeToken<List<String>> token = new TypeToken<List<String>>() {};
TypeToken<List<? extends CharSequence>> token = new TypeToken<List<? extends CharSequence>>() {};
<T> void test() {
TypeToken<T[]> token = new TypeToken<T[]>() {};
}
type
是
GenericArrayType
拿着
TypeVariable
作为其组件类型。这完全没问题。
TypeToken
时在 lambda 表达式中,事情开始发生变化。 (类型变量来自上面的
test
函数)
Supplier<TypeToken<T[]>> sup = () -> new TypeToken<T[]>() {};
type
仍然是
GenericArrayType
,但它持有
null
作为其组件类型。
Supplier<TypeToken<T[]>> sup = new Supplier<TypeToken<T[]>>() {
@Override
public TypeToken<T[]> get() {
return new TypeToken<T[]>() {};
}
};
:=
或
Fi0
是什么让我们很难详细了解它。如果有人能澄清一下,如果这可能是对奇怪行为的解释,我会很高兴。
null
而不是最慷慨的类型,Object。我想不出语言设计者决定这样做的单一原因。
8u191
)。令我遗憾的是,这并没有改变任何东西,尽管 Java 的类型推断得到了改进......
最佳答案
tldr:
- There is a bug in
javac
that records the wrong enclosing method for lambda-embedded inner classes. As a result, type variables on the actual enclosing method cannot be resolved by those inner classes.- There are arguably two sets of bugs in the
java.lang.reflect
API implementation:
- Some methods are documented as throwing exceptions when nonexistent types are encountered, but they never do. Instead, they allow null references to propagate.
- The various
Type::toString()
overrides currently throw or propagate aNullPointerException
when a type cannot be resolved.
Signature
包含类父类(super class)型的完全参数化泛型签名的属性。我已经
written about these before ,但简短的解释是:没有它们,除非您碰巧拥有源代码,否则不可能将泛型类型用作泛型类型。由于类型删除,有关类型变量的信息在编译时丢失。如果该信息不作为额外元数据包含在内,则 IDE 和您的编译器都不会知道类型是泛型的,并且您不能这样使用它。编译器也不能发出必要的运行时检查来强制类型安全。
javac
将为签名包含类型变量或参数化类型的任何类型或方法发出泛型签名元数据,这就是为什么您能够获取匿名类型的原始泛型父类(super class)型信息的原因。例如,这里创建的匿名类型:
TypeToken<?> token = new TypeToken<List<? extends CharSequence>>() {};
Signature
:
LTypeToken<Ljava/util/List<+Ljava/lang/CharSequence;>;>;
java.lang.reflection
API 可以解析关于您的(匿名)类的通用父类(super class)型信息。
TypeToken
时,这工作得很好。用具体类型参数化。让我们看一个更相关的例子,它的类型参数包含一个类型变量:
static <F> void test() {
TypeToken sup = new TypeToken<F[]>() {};
}
LTypeToken<[TF;>;
java.lang.reflect
API 能够从这些签名中提取通用父类(super class)型信息。如果我们查看
Class::getGenericSuperclass()
,我们看到它做的第一件事是调用
getGenericInfo()
.如果我们之前没有调用过这个方法,一个
ClassRepository
被实例化:
private ClassRepository getGenericInfo() {
ClassRepository genericInfo = this.genericInfo;
if (genericInfo == null) {
String signature = getGenericSignature0();
if (signature == null) {
genericInfo = ClassRepository.NONE;
} else {
// !!! RELEVANT LINE HERE: !!!
genericInfo = ClassRepository.make(signature, getFactory());
}
this.genericInfo = genericInfo;
}
return (genericInfo != ClassRepository.NONE) ? genericInfo : null;
}
getFactory()
, 扩展为:
CoreReflectionFactory.make(this, ClassScope.make(this))
ClassScope
是我们关心的一点:这为类型变量提供了一个解析范围。给定一个类型变量名称,在作用域中搜索匹配的类型变量。如果未找到,则搜索“外部”或封闭范围:
public TypeVariable<?> lookup(String name) {
TypeVariable<?>[] tas = getRecvr().getTypeParameters();
for (TypeVariable<?> tv : tas) {
if (tv.getName().equals(name)) {return tv;}
}
return getEnclosingScope().lookup(name);
}
ClassScope
):
protected Scope computeEnclosingScope() {
Class<?> receiver = getRecvr();
Method m = receiver.getEnclosingMethod();
if (m != null)
// Receiver is a local or anonymous class enclosed in a method.
return MethodScope.make(m);
// ...
}
F
)(例如匿名
TypeToken<F[]>
),则下一步是搜索
封闭方法 .如果我们查看反汇编的匿名类,我们会看到这个属性:
EnclosingMethod: LambdaTest.test()V
computeEnclosingScope
将产生
MethodScope
用于泛型方法
static <F> void test()
.自
test
声明类型变量
W
,我们在搜索封闭范围时找到它。
invokedynamic
指令被发出,这会导致
TypeToken
我们第一次点击该指令时生成的实现类。
private static /* synthetic */ Object lambda$test$0() {
return new LambdaTest$1();
}
LambdaTest$1
是你的匿名类(class)。让我们分解它并检查我们的属性:
Signature: LTypeToken<TW;>;
EnclosingMethod: LambdaTest.lambda$test$0()Ljava/lang/Object;
W
.
但是EnclosingMethod
指合成方法 .
lambda$test$0()
不声明类型变量
W
.此外,
lambda$test$0()
没有被
test()
包围,所以
W
的声明在里面是不可见的。你的匿名类有一个父类(super class)型,它包含一个你的类不知道的类型变量,因为它超出了范围。
getGenericSuperclass()
,
LambdaTest$1
的范围层次结构不包含
W
,因此解析器无法解析它。由于代码的编写方式,这个未解析的类型变量导致
null
被放置在泛型父类(super class)型的类型参数中。
TypeToken<String>
)那么你就不会遇到这个问题。
javac
中存在错误. Java 虚拟机规范
§4.7.7 (“
EnclosingMethod
属性”)指出:
It is the responsibility of a Java compiler to ensure that the method identified via the
method_index
is indeed the closest lexically enclosing method of the class that contains thisEnclosingMethod
attribute. (emphasis mine)
javac
似乎在 lambda 重写器运行后确定封闭方法,因此,
EnclosingMethod
属性指的是一个在词法范围内从未存在过的方法。如
EnclosingMethod
报告了实际的词法封闭方法,该方法上的类型变量可以由 lambda 嵌入的类解析,并且您的代码将产生预期的结果。
null
也是一个错误。要传播到
ParameterizedType
中的类型参数(正如@tom-hawtin-tackline 指出的那样,它具有诸如
toString()
抛出 NPE 之类的辅助效果)。
EnclosingMethod
问题现已上线。
java.lang.reflect
中存在多个错误及其支持的 API。
ParameterizedType::getActualTypeArguments()
被记录为抛出
TypeNotPresentException
当“任何实际类型参数引用不存在的类型声明”时。该描述可以说涵盖了类型变量不在范围内的情况。
GenericArrayType::getGenericComponentType()
当“底层数组类型的类型引用不存在的类型声明”时,应该抛出类似的异常。目前,两者似乎都没有抛出
TypeNotPresentException
在任何情况下。
Type::toString
覆盖应该只填写任何未解析类型的规范名称,而不是抛出 NPE 或任何其他异常。
class Test<X> {
void test() {
Supplier<TypeToken<X>> s1 = () -> new TypeToken<X>() {};
Supplier<TypeToken<String>> s2 = () -> new TypeToken<String>() {};
Supplier<TypeToken<List<String>>> s3 = () -> new TypeToken<List<String>>() {};
}
}
关于java - 使用 lambda 而不是显式匿名内部类时的不同泛型行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53039980/
我有 table 像这样 -------------------------------------------- id size title priority
我的应用在不同的 Activity (4 个 Activity )中仅包含横幅广告。所以我的疑问是, 我可以对所有横幅广告使用一个广告单元 ID 吗? 或者 每个 Activity 使用不同的广告单元
我有任意(但统一)数字列表的任意列表。 (它们是 n 空间中 bin 的边界坐标,我想绘制其角,但这并不重要。)我想生成所有可能组合的列表。所以:[[1,2], [3,4],[5,6]] 产生 [[1
我刚刚在学校开始学习 Java,正在尝试自定义控件和图形。我目前正在研究图案锁,一开始一切都很好,但突然间它绘制不正确。我确实更改了一些代码,但是当我看到错误时,我立即将其更改回来(撤消,ftw),但
在获取 Distinct 的 Count 时,我在使用 Group By With Rollup 时遇到了一个小问题。 问题是 Rollup 摘要只是所有分组中 Distinct 值的总数,而不是所有
这不起作用: select count(distinct colA, colB) from mytable 我知道我可以通过双选来简单地解决这个问题。 select count(*) from (
这个问题在这里已经有了答案: JavaScript regex whitespace characters (5 个回答) 2年前关闭。 你能解释一下为什么我会得到 false比较 text ===
这个问题已经有答案了: 奥 git _a (56 个回答) 已关闭 9 年前。 我被要求用 Javascript 编写一个函数 sortByFoo 来正确响应此测试: // Does not cras
所以,我不得不说,SQL 是迄今为止我作为开发人员最薄弱的一面。也许我想要完成的事情很简单。我有这样的东西(这不是真正的模型,但为了使其易于理解而不浪费太多时间解释它,我想出了一个完全模仿我必须使用的
这个问题在这里已经有了答案: How does the "this" keyword work? (22 个回答) 3年前关闭。 简而言之:为什么在使用 Objects 时,直接调用的函数和通过引用传
这个问题在这里已经有了答案: 关闭 12 年前。 Possible Duplicate: what is the difference between (.) dot operator and (-
我真的不明白这里发生了什么但是: 当我这样做时: colorIndex += len - stopPos; for(int m = 0; m < len - stopPos; m++) { c
思考 MySQL 中的 Group By 函数的最佳方式是什么? 我正在编写一个 MySQL 查询,通过 ODBC 连接在 Excel 的数据透视表中提取数据,以便用户可以轻松访问数据。 例如,我有:
我想要的SQL是这样的: SELECT week_no, type, SELECT count(distinct user_id) FROM group WHERE pts > 0 FROM bas
商店表: +--+-------+--------+ |id|name |date | +--+-------+--------+ |1 |x |Ma
对于 chrome 和 ff,当涉及到可怕的 ie 时,这个脚本工作完美。有问题 function getY(oElement) { var curtop = 0; if (oElem
我现在无法提供代码,因为我目前正在脑海中研究这个想法并在互联网上四处乱逛。 我了解了进程间通信和使用共享内存在进程之间共享数据(特别是结构)。 但是,在对保存在不同 .c 文件中的程序使用 fork(
我想在用户集合中使用不同的功能。在 mongo shell 中,我可以像下面这样使用: db.users.distinct("name"); 其中名称是用于区分的集合字段。 同样我想要,在 C
List nastava_izvjestaj = new List(); var data_context = new DataEvidencijaDataContext();
我的 Rails 应用程序中有 Ransack 搜索和 Foundation,本地 css 渲染正常,而生产中的同一个应用程序有一个怪癖: 应用程序中的其他内容完全相同。 我在 Chrome 和 Sa
我是一名优秀的程序员,十分优秀!