检查谓词泛型的类型
Check type of Predicate generic
我有 2 个 类,它们的内部构造根本不重要。
class ClassA {
//...
}
class ClassB {
//...
}
我有 2 个使用这些 类 的谓词,假设它们看起来像这样
private Predicate<ClassA> classAPredicate() {
return Objects::nonNull;
}
private Predicate<ClassB> classBPredicate() {
return Objects::nonNull;
}
现在,我在外部库中使用了已经被许多用户使用的通用方法,不幸的是,它有非常通用的输入参数,即 Object
,在 90% 的情况下是 Predicate
.
我需要做的是通过检查传递的类型 Predicate
并在此基础上执行一些操作来扩展此方法的功能。
public void test(Object obj) {
Predicate predicate = (Predicate)obj;
if(predicate.getClass().isAssignableFrom(ClassA.class)) {
System.out.println(predicate.test(new ClassA()));
// logic specific to Predicate<ClassA>
} else {
System.out.println(predicate.test(new ClassB()));
// logic specific to Predicate<ClassB>
}
}
但是,在测试期间,我通过了 Predicates
并且失败了 Exception in thread "main" java.lang.ClassCastException:
test(classAPredicate());
test(classBPredicate());
我一直在调试,isAssignableFrom()
总是返回 false,所以这里的错误很明显。我不确定这是否是正确的方法,但我还没有想出任何其他方法。有什么方法可以检查 Predicate
的类型吗?
我知道我要实现的并不理想,但这是当前的要求...
在上面,谓词 class 不能从 Class A 赋值。
if(predicate.getClass().isAssignableFrom(ClassA.class))
这导致 else 条件 运行 将 B 的实例传递给类型 A 的谓词,从而导致强制转换异常。由于类型擦除,要确定是否应将 A 或 B 的实例传递给谓词并不容易。 3 个选项是:
- 尝试每种输入类型,直到不抛出 ClassCastException。
- 在新方法而不是现有测试函数中处理预期行为。
- 定义一个比 Predicate 更具体的接口,它也有一个方法来获取谓词测试的类型并在条件中使用测试类型。例如:
public interface TypedPredicate<T> extends Predicate<T> { Class<T> getTestType(); }
嗯,
我一直在做 Java 泛型,现在已经三年了。我可以在这里引用十几个关于“Reifying Java Generics”的 Stack Overflow 帖子:SO1,, SO3。最重要的是,如果你打算写 Java 年复一年,你必须知道 "Generic Type Parameter"
只是 NOT ACCESSIBLE 在 Run-Time 没有字段,或者没有额外的方法来检索它们。 Java 泛型(看起来像这样的语法:STUFF<TYPE>
和 greater-than、less-than 符号是 STRICTLY A COMPILE-TIME 特征)。在运行时,JRE 根本不知道 Type-Parameter 的类型是什么 - 它所能做的就是在尝试滥用时抛出 ClassCastException
。
注意: 'Misuse' 抛出 ClassCastException
的泛型类型听起来很奇怪,如果您认为 JRE 不知道也不知道关心类型参数的类型是什么。大多数情况下,抛出异常的方式是这样的,如果您在泛型中编写的代码进行了假设,并且如果它做出了错误的假设,那么将抛出此异常。
阅读 Sun / Oracle 关于“Reifying Generic Type Parameters." Also, most importantly, this concept has a very real name that you should read about all the time in Java - and it is called "Run Time Type Erasure” 的“待办事项”列表 在此 Stack Overflow Answer 之前发布的解决方案说使用 try-catch (ClassCastException)
块,实际上,一个有效的答案。
另外: 如果您打算以任何预期的方式使用您的 TypedPredicate<T>
,关于创建此类 TypedPredicate<T> extends Predicate<T>
的答案不是正确的答案允许 Java Lambda Syntax 使用它。当您添加以下方法时:
public interface TypedPredicate extends Predicate { Class
getTestType(); }
您将无法使用语法 @FunctionalInterface
- 这是 class java.util.function.Predicate<T>
此外,还有一个更严重的问题,程序员无法访问 T
的类型,并且在运行时不知道由 JRE
你在这里看到这部分(因为答案有一个绿色的复选标记):
{ Class<T> getTestType(); }
// Can you answer what you would write inside the method body of this
// 'extra-method' that you have added to Predicate<T> ???
如果没有构造函数,则无法实例化扩展 "Predicate"
的 class 的以下实现。它不能被称为 "@FunctionalInterface"
并且 lambda-expression
不能用于创建它们:
// @FunctionalInterface (Commented Out)
public class TypedPredicate<A> implements Predicate<A>
{
public boolean test(A a) { return pred.test(a); }
// This is how the "Class of A" becomes accessible. It this
// version it is a public (and final) field.
public final Class<A> className;
// NOTE: This is the most important part here, the class of
// Variable-Type Parameter 'A' must be passed as a parameter
// to the constructor. The programmer *LITERALLY* has to tell
// the code what type 'A' actually is! This is the *BANE* of
// what I call the Java-Erasure-Fiasco programming.
public TypedPredicate(Predicate<A> pred, Class<A> className)
{
this.pred = pred;
this.className = className;
}
// Again, because a constructor is necessary, this cannot be
// called a "Functional Interface" and it will not work very
// much like a java.util.function.Predicate<T>, but it will
// indeed implement the interface.
}
最好的解决方案是重新调整您拥有的任何逻辑,这样您就不需要 guess
谓词是什么类型!下一个最好的办法是尝试上一个答案中建议的 catch (ClassCastException)
版本。
最后: 关于 java.lang.Class.isAssignableFrom(...)
这个想法背后有正确的想法 - 但仅如果 你实际上有 Class<T> clazz
作为你面前的一个实例,可以这么说。获取 Class<T>
实例的唯一方法是将其传递给构造函数,就像我发布的示例中那样。
我有 2 个 类,它们的内部构造根本不重要。
class ClassA {
//...
}
class ClassB {
//...
}
我有 2 个使用这些 类 的谓词,假设它们看起来像这样
private Predicate<ClassA> classAPredicate() {
return Objects::nonNull;
}
private Predicate<ClassB> classBPredicate() {
return Objects::nonNull;
}
现在,我在外部库中使用了已经被许多用户使用的通用方法,不幸的是,它有非常通用的输入参数,即 Object
,在 90% 的情况下是 Predicate
.
我需要做的是通过检查传递的类型 Predicate
并在此基础上执行一些操作来扩展此方法的功能。
public void test(Object obj) {
Predicate predicate = (Predicate)obj;
if(predicate.getClass().isAssignableFrom(ClassA.class)) {
System.out.println(predicate.test(new ClassA()));
// logic specific to Predicate<ClassA>
} else {
System.out.println(predicate.test(new ClassB()));
// logic specific to Predicate<ClassB>
}
}
但是,在测试期间,我通过了 Predicates
并且失败了 Exception in thread "main" java.lang.ClassCastException:
test(classAPredicate());
test(classBPredicate());
我一直在调试,isAssignableFrom()
总是返回 false,所以这里的错误很明显。我不确定这是否是正确的方法,但我还没有想出任何其他方法。有什么方法可以检查 Predicate
的类型吗?
我知道我要实现的并不理想,但这是当前的要求...
在上面,谓词 class 不能从 Class A 赋值。
if(predicate.getClass().isAssignableFrom(ClassA.class))
这导致 else 条件 运行 将 B 的实例传递给类型 A 的谓词,从而导致强制转换异常。由于类型擦除,要确定是否应将 A 或 B 的实例传递给谓词并不容易。 3 个选项是:
- 尝试每种输入类型,直到不抛出 ClassCastException。
- 在新方法而不是现有测试函数中处理预期行为。
- 定义一个比 Predicate 更具体的接口,它也有一个方法来获取谓词测试的类型并在条件中使用测试类型。例如:
public interface TypedPredicate<T> extends Predicate<T> { Class<T> getTestType(); }
嗯,
我一直在做 Java 泛型,现在已经三年了。我可以在这里引用十几个关于“Reifying Java Generics”的 Stack Overflow 帖子:SO1,"Generic Type Parameter"
只是 NOT ACCESSIBLE 在 Run-Time 没有字段,或者没有额外的方法来检索它们。 Java 泛型(看起来像这样的语法:STUFF<TYPE>
和 greater-than、less-than 符号是 STRICTLY A COMPILE-TIME 特征)。在运行时,JRE 根本不知道 Type-Parameter 的类型是什么 - 它所能做的就是在尝试滥用时抛出 ClassCastException
。
注意: 'Misuse' 抛出 ClassCastException
的泛型类型听起来很奇怪,如果您认为 JRE 不知道也不知道关心类型参数的类型是什么。大多数情况下,抛出异常的方式是这样的,如果您在泛型中编写的代码进行了假设,并且如果它做出了错误的假设,那么将抛出此异常。
阅读 Sun / Oracle 关于“Reifying Generic Type Parameters." Also, most importantly, this concept has a very real name that you should read about all the time in Java - and it is called "Run Time Type Erasure” 的“待办事项”列表 在此 Stack Overflow Answer 之前发布的解决方案说使用 try-catch (ClassCastException)
块,实际上,一个有效的答案。
另外: 如果您打算以任何预期的方式使用您的 TypedPredicate<T>
,关于创建此类 TypedPredicate<T> extends Predicate<T>
的答案不是正确的答案允许 Java Lambda Syntax 使用它。当您添加以下方法时:
public interface TypedPredicate extends Predicate { Class getTestType(); }
您将无法使用语法 @FunctionalInterface
- 这是 class java.util.function.Predicate<T>
此外,还有一个更严重的问题,程序员无法访问 T
的类型,并且在运行时不知道由 JRE
你在这里看到这部分(因为答案有一个绿色的复选标记):
{ Class<T> getTestType(); }
// Can you answer what you would write inside the method body of this
// 'extra-method' that you have added to Predicate<T> ???
如果没有构造函数,则无法实例化扩展 "Predicate"
的 class 的以下实现。它不能被称为 "@FunctionalInterface"
并且 lambda-expression
不能用于创建它们:
// @FunctionalInterface (Commented Out)
public class TypedPredicate<A> implements Predicate<A>
{
public boolean test(A a) { return pred.test(a); }
// This is how the "Class of A" becomes accessible. It this
// version it is a public (and final) field.
public final Class<A> className;
// NOTE: This is the most important part here, the class of
// Variable-Type Parameter 'A' must be passed as a parameter
// to the constructor. The programmer *LITERALLY* has to tell
// the code what type 'A' actually is! This is the *BANE* of
// what I call the Java-Erasure-Fiasco programming.
public TypedPredicate(Predicate<A> pred, Class<A> className)
{
this.pred = pred;
this.className = className;
}
// Again, because a constructor is necessary, this cannot be
// called a "Functional Interface" and it will not work very
// much like a java.util.function.Predicate<T>, but it will
// indeed implement the interface.
}
最好的解决方案是重新调整您拥有的任何逻辑,这样您就不需要 guess
谓词是什么类型!下一个最好的办法是尝试上一个答案中建议的 catch (ClassCastException)
版本。
最后: 关于 java.lang.Class.isAssignableFrom(...)
这个想法背后有正确的想法 - 但仅如果 你实际上有 Class<T> clazz
作为你面前的一个实例,可以这么说。获取 Class<T>
实例的唯一方法是将其传递给构造函数,就像我发布的示例中那样。