Static Generic Methods, Type Inference causes java.lang.VerifyError: Verifier rejected class
Static Generic Methods, Type Inference causes java.lang.VerifyError: Verifier rejected class
考虑这些经典的 Person 多态层次结构...
超级接口
public interface Person {
String getName();
interface Builder<P, B extends Builder<P, B>> {
B name(String name);
P build();
}
}
Student.java
public class Student implements Person {
@Override
public String getName() {
...
}
public static class Builder implements Person.Builder<Student, Builder> {
@Override
public Builder name(String name) {
...
}
@Override
public Student build() {
...
}
}
}
Employee.java
我在这里省略了Employee类型,因为它和student一样
PersonBuilderFactory.java
public class PersonBuilderFactory {
public static <T extends Person.Builder> T getBuilder(T...args) {
if (args.getClass().getComponentType().isAssignableFrom(Student.Builder.class)) {
return (T) new Student.Builder();
} else if (args.getClass().getComponentType().isAssignableFrom(Employee.Builder.class)) {
return (T) new Employee.Builder();
}
throw new RuntimeException("No such builder for other person types");
}
TestInference.java
public class TestInference {
public static void testInference() {
Compile Test 1
// java wants me to infer the type argument
// which will build perfectly fine
Student.Builder studentBuilder1 = PersonBuilderFactory.<Student.Builder>getBuilder();
Employee.Builder employeeBuilder1 = PersonBuilderFactory.<Employee.Builder>getBuilder();
Compile Test 2
// so if i remove those explicit type arguments
// everything still works fine
Student.Builder studentBuilder2 = PersonBuilderFactory.getBuilder(); // this will implicitly get a student builder
Employee.Builder employeeBuilder2 = PersonBuilderFactory.getBuilder(); // this will have an employee builder
Compile Test 3
// Now...
// leaving the argument type NOT inferred and changing the data type
// gives me a compile error which is Im expecting and I want
String someString = PersonBuilderFactory.<Student.Builder>getBuilder();
Compile Test 4
// but when i remove the explicit type argument
// it compiles but this will cause me a specific Verify Error
String someAnotherString = PersonBuilderFactory.getBuilder();
}
}
编译测试 1 有明确的类型参数,java 要求我删除(告诉我它有点冗余)导致编译测试 2,效果很好
编译测试 3 是我所期望的 if
- 我声明了一个不在变量声明类型范围内的显式类型参数
编译测试 4 让我感到困惑,为什么编译器没有说它返回的东西没有 Person.Builder (T extends Person.Builder),当我 运行 代码时,它抛出一个验证错误告诉我我有一个错误的方法,我的问题是为什么编译器没有?但是当我明确指定一个类型参数时它确实如此,当我声明一个正确的变量数据类型时,它 returns 一个正确的类型。
我在这里很难理解类型推断,有问题说它与 Java 编译器版本有关,另一个 post 说 "you just have a very complex method"
如有任何帮助,我们将不胜感激。
when I ran the code, it throws me a Verify Error that tells me I have a bad method, my question is why the compiler didnt?
因为编译器不能确定你做错了什么。
问题是 Builder
是一个接口;请注意,如果将 Builder
变成 class,则代码根本无法编译。关于 String
是最终的 class 这一事实,类型推断不会以特殊的方式对待它(Chapter 18 of JLS 只在单词 "finally" 中提到 "final" ; 它根本没有提到 final
)。
所以它允许案例 4,因为 class 比如:
class Something extends String implements Person.Builder {}
就类型推断而言,是可能的。类似地,<T extends String>
是允许的,即使没有扩展 String
.
该行的推断类型是交集类型:
INT#1 extends String,Builder
(我通过 -Xlint:unchecked
' 编译得到这个;方便的是,那里有一个关于未经检查的通用数组创建的警告)
因此编译器似乎允许它。
但是,在方法中,args.getClass().getComponentType()
的值是java.lang.String
;这与您的任何条件都不匹配,因此它在底部遇到异常。
考虑这些经典的 Person 多态层次结构...
超级接口
public interface Person {
String getName();
interface Builder<P, B extends Builder<P, B>> {
B name(String name);
P build();
}
}
Student.java
public class Student implements Person {
@Override
public String getName() {
...
}
public static class Builder implements Person.Builder<Student, Builder> {
@Override
public Builder name(String name) {
...
}
@Override
public Student build() {
...
}
}
}
Employee.java 我在这里省略了Employee类型,因为它和student一样
PersonBuilderFactory.java
public class PersonBuilderFactory {
public static <T extends Person.Builder> T getBuilder(T...args) {
if (args.getClass().getComponentType().isAssignableFrom(Student.Builder.class)) {
return (T) new Student.Builder();
} else if (args.getClass().getComponentType().isAssignableFrom(Employee.Builder.class)) {
return (T) new Employee.Builder();
}
throw new RuntimeException("No such builder for other person types");
}
TestInference.java
public class TestInference {
public static void testInference() {
Compile Test 1
// java wants me to infer the type argument
// which will build perfectly fine
Student.Builder studentBuilder1 = PersonBuilderFactory.<Student.Builder>getBuilder();
Employee.Builder employeeBuilder1 = PersonBuilderFactory.<Employee.Builder>getBuilder();
Compile Test 2
// so if i remove those explicit type arguments
// everything still works fine
Student.Builder studentBuilder2 = PersonBuilderFactory.getBuilder(); // this will implicitly get a student builder
Employee.Builder employeeBuilder2 = PersonBuilderFactory.getBuilder(); // this will have an employee builder
Compile Test 3
// Now...
// leaving the argument type NOT inferred and changing the data type
// gives me a compile error which is Im expecting and I want
String someString = PersonBuilderFactory.<Student.Builder>getBuilder();
Compile Test 4
// but when i remove the explicit type argument
// it compiles but this will cause me a specific Verify Error
String someAnotherString = PersonBuilderFactory.getBuilder();
}
}
编译测试 1 有明确的类型参数,java 要求我删除(告诉我它有点冗余)导致编译测试 2,效果很好
编译测试 3 是我所期望的 if
- 我声明了一个不在变量声明类型范围内的显式类型参数
编译测试 4 让我感到困惑,为什么编译器没有说它返回的东西没有 Person.Builder (T extends Person.Builder),当我 运行 代码时,它抛出一个验证错误告诉我我有一个错误的方法,我的问题是为什么编译器没有?但是当我明确指定一个类型参数时它确实如此,当我声明一个正确的变量数据类型时,它 returns 一个正确的类型。
我在这里很难理解类型推断,有问题说它与 Java 编译器版本有关,另一个 post 说 "you just have a very complex method"
如有任何帮助,我们将不胜感激。
when I ran the code, it throws me a Verify Error that tells me I have a bad method, my question is why the compiler didnt?
因为编译器不能确定你做错了什么。
问题是 Builder
是一个接口;请注意,如果将 Builder
变成 class,则代码根本无法编译。关于 String
是最终的 class 这一事实,类型推断不会以特殊的方式对待它(Chapter 18 of JLS 只在单词 "finally" 中提到 "final" ; 它根本没有提到 final
)。
所以它允许案例 4,因为 class 比如:
class Something extends String implements Person.Builder {}
就类型推断而言,是可能的。类似地,<T extends String>
是允许的,即使没有扩展 String
.
该行的推断类型是交集类型:
INT#1 extends String,Builder
(我通过 -Xlint:unchecked
' 编译得到这个;方便的是,那里有一个关于未经检查的通用数组创建的警告)
因此编译器似乎允许它。
但是,在方法中,args.getClass().getComponentType()
的值是java.lang.String
;这与您的任何条件都不匹配,因此它在底部遇到异常。