我在处理泛型类型的可能值时遇到问题。我想要一个 TimeTreeNodeDTO 应该能够处理两种类型的 TimeTree。具有 int 值的时间树和具有 double 值的时间树。为此,我添加了一个通用 T(扩展 Number 以毫无问题地处理 Inger 和 Double)。
public class TimeTreeNodeDTO<T extends Number>
{
// ....
private T value;
private List<TimeTreeNodeDTO<T>> children;
public TimeTreeNodeDTO(PeriodType periodType, List<TimeTreeNodeDTO<T>> children) {
this.periodType = periodType;
this.value = calculateValue(children);
this.children = children;
}
private T calculateValue(List<TimeTreeNodeDTO<T>> children) {
if(children == null || children.isEmpty())
{
return null;
}
if (value instanceof Integer) {
int sum = children.stream()
.mapToInt(node -> node.getValue().intValue())
.sum();
return (T) Integer.valueOf(sum);
} else if (value instanceof Double) {
double sum = children.stream()
.mapToDouble(node -> node.getValue().doubleValue())
.sum();
return (T) Double.valueOf(sum);
} else {
throw new IllegalArgumentException("Unsupported type for value : " + value.getClass().getName());
}
}
// ....
}
到目前为止我认为很好(也许可以有更好的解决方案)。当我尝试实例化这种情况时
TimeTreeNodeDTO<Integer> wn1 = new TimeTreeNodeDTO<>(PeriodType.WEEK, List.of(
new TimeTreeNodeDTO<>(PeriodType.DAY,1),
new TimeTreeNodeDTO<>(PeriodType.DAY,2),
new TimeTreeNodeDTO<>(PeriodType.DAY,3),
new TimeTreeNodeDTO<>(PeriodType.DAY,4),
new TimeTreeNodeDTO<>(PeriodType.DAY,5),
new TimeTreeNodeDTO<>(PeriodType.DAY,6),
new TimeTreeNodeDTO<>(PeriodType.DAY,7)));
java.lang.NullPointerException
at artelys.crystal.pdm.opus.timetreelib.dto.TimeTreeNodeDTO.calculateValue(TimeTreeNodeDTO.java:65)
at artelys.crystal.pdm.opus.timetreelib.dto.TimeTreeNodeDTO.<init>(TimeTreeNodeDTO.java:33)
at artelys.crystal.pdm.opus.timetreelib.TestTimeTreeDTO.setUp(TestTimeTreeDTO.java:29)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:725)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:126)
at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:76)
at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:506)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$21(ClassBasedTestDescriptor.java:491)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:171)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:199)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:199)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:168)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:66)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1541)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:57)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:232)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:55)
在 TimeTreeNodeDTO 类中运行此行时,我基本上遇到了 NullPointerException
throw new IllegalArgumentException("Unsupported type for value : " + value.getClass().getName());
发生这种情况是因为 value 为空。我知道 is null,我想在这个方法中计算该值,但因为不是 Integer 或 Double,所以它直接进入 else 语句。有没有办法检查 T 值的类型,即使该值为 null?
我尝试将 T 值“初始化”为 0,但我不能,因为它是泛型。我不知道我还能做什么。
我不知道也许这是一种不好的方法,可以通过其他方式解决。
希望你能帮助我:)
这是完全不可能的;泛型是编译器想象出来的。它的存在是为了将事物结合在一起。例如,给出以下代码:
public static Object nullCheck(Object o) {
if (o == null) throw new NullPointerException();
return o;
}
// to use:
class Example {
private String name;
public void setName(String name) {
this.name = nullCheck(name);
}
}
这当然很好。然而,这不会编译 - 而 code 保证你的
nullCheck
方法要么返回你给它的相同的东西(所以,那个“东西”与你给它的类型相同),要么抛出,那就是只是在代码中。从设计上来说,Java 假设代码不是一次性编写就不再更改的。 signatures(名称、参数类型、返回类型等)是您不应该轻易更改的内容(然后需要重新编译各种代码),但您可以很好地更改主体。
这就是为什么泛型存在的原因,是为了链接事物。这确实有效:
public static <T> T nullCheck(T o) {
if (o == null) throw new NullPointerException();
return o;
}
作为类文件,上面的内容与早期版本“完全相同”,除了类文件知道泛型,但 JVM (java.exe
) 实际上不知道泛型是什么。这就像对它的评论。
它知道两个 T 是链接在一起的:传递给
nullCheck
this.name = nullCheck(name)
this.name = (String) nullCheck(name)
。编译器知道需要强制执行此“保证”(arg 和返回类型相同);如果您尝试编写 nullCheck
那些类型参数没有具体化。
new ArrayList<String>()
和
new ArrayList<Integer>()
的执行是 100% 相同- 该 arraylist 不知道泛型是什么。 你的错误
value
方法中使用
calculateValue(List<..> children)
。你不应该这样做;你应该使用 child.value
(其中 child 是该列表中的某个项目)。解决方案
Class<T>
作为这个东西的构造函数的一部分传递,因此现在你知道了。这真的很难看 -
j.l.Class
和泛型并不完全相同:基元可以表示为 j.l.Class
对象(Class<?> x = int.class;
工作正常),但泛型不能(List<int> foo;
是编译器错误)。泛型可以嵌套(List<Set<String>>
很好),但这在j.l.Class
实例中不起作用 - 这些实例不能携带泛型,所以你不能比Set.class
更进一步 - 你不能存储那)。即使here你认为你不会遇到它,必须显式传递类型意味着你的调用者必须重复自己,这是令人讨厌的。不要这样做。 更好的解决办法就是不举报。您需要在代码中的大 if 子句批处理上使用另一个
else
:如果子项的
value
不是 Integer,也不是 Double,但也不是 null
,您可以提及类型。如果是 null
,那么这不是“我不支持该类型”的问题 - 不,问题是:“不知何故,您设法创建了一个值为 null
的节点,但根本不支持该节点”。