考虑下面的代码
public class JDK10Test {
public static void main(String[] args) {
Double d = false ? 1.0 : new HashMap<String, Double>().get("1");
System.out.println(d);
}
}
在 JDK8 上运行时,此代码打印 null 而在 JDK10 上,此代码导致 NullPointerException
Exception in thread "main" java.lang.NullPointerException
at JDK10Test.main(JDK10Test.java:5)
编译器生成的字节码几乎相同,除了 JDK10 编译器生成的与自动装箱相关并且似乎负责 NPE 的两条附加指令。
15: invokevirtual #7 // Method java/lang/Double.doubleValue:()D
18: invokestatic #8 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
这种行为是 JDK10 中的错误还是故意更改以使行为更严格?
JDK8: java version "1.8.0_172"
JDK10: java version "10.0.1" 2018-04-17
最佳答案
我相信这是一个似乎已修复的错误。 throw NullPointerException根据 JLS,这似乎是正确的行为。
我认为这里发生的情况是,出于某种原因,在版本 8 中,编译器考虑了方法返回类型提到的类型变量的边界,而不是实际的类型参数。换句话说,它认为 ...get("1")返回 Object .这可能是因为它正在考虑方法的删除,或者其他一些原因。
行为应该取决于 get 的返回类型方法,由 §15.26 的以下摘录指定:
If both the second and the third operand expressions are numeric expressions, the conditional expression is a numeric conditional expression.
For the purpose of classifying a conditional, the following expressions are numeric expressions:
[…]
A method invocation expression (§15.12) for which the chosen most specific method (§15.12.2.5) has a return type that is convertible to a numeric type.
Note that, for a generic method, this is the type before instantiating the method's type arguments.
[…]
Otherwise, the conditional expression is a reference conditional expression.
[…]
The type of a numeric conditional expression is determined as follows:
[…]
If one of the second and third operands is of primitive type
T, and the type of the other is the result of applying boxing conversion (§5.1.7) toT, then the type of the conditional expression isT.
换句话说,如果两个表达式都可以转换为数字类型,并且一个是原始的,另一个是装箱的,那么三元条件的结果类型就是原始类型。
(表 15.25-C 还方便地向我们展示了三元表达式 boolean ? double : Double 的类型确实是 double,这再次意味着拆箱和 throw 是正确的。)
如果 get 的返回类型方法不可转换为数值类型,则三元条件将被视为“引用条件表达式”,不会发生拆箱。
另外,我认为注释“对于泛型方法,这是实例化方法的类型参数之前的类型” 不应该适用于我们的案例。 Map.get不声明类型变量,so it's not a generic method by the JLS' definition .但是,此注释是在 Java 9 中添加的(唯一的更改,see JLS8),因此它可能与我们今天看到的行为有关。
对于 HashMap<String, Double> ,返回类型为get 应该是Double .
这是一个支持我的理论的 MCVE,即编译器正在考虑类型变量边界而不是实际类型参数:
class Example<N extends Number, D extends Double> {
N nullAsNumber() { return null; }
D nullAsDouble() { return null; }
public static void main(String[] args) {
Example<Double, Double> e = new Example<>();
try {
Double a = false ? 0.0 : e.nullAsNumber();
System.out.printf("a == %f%n", a);
Double b = false ? 0.0 : e.nullAsDouble();
System.out.printf("b == %f%n", b);
} catch (NullPointerException x) {
System.out.println(x);
}
}
}
The output of that program on Java 8是:
a == null
java.lang.NullPointerException
换句话说,尽管 e.nullAsNumber()和 e.nullAsDouble()具有相同的实际返回类型,只有 e.nullAsDouble()被视为“数字表达式”。方法之间的唯一区别是类型变量绑定(bind)。
可能需要进行更多调查,但我想发布我的调查结果。我尝试了很多东西,发现该错误(即没有拆箱/NPE)似乎仅在表达式是返回类型中具有类型变量的方法时才会发生。
有趣的是,我发现 the following program also throws在 Java 8 中:
import java.util.*;
class Example {
static void accept(Double d) {}
public static void main(String[] args) {
accept(false ? 1.0 : new HashMap<String, Double>().get("1"));
}
}
这表明编译器的行为实际上是不同的,这取决于三元表达式是分配给局部变量还是方法参数。
(最初我想使用重载来证明编译器赋予三元表达式的实际类型,但鉴于上述差异,这看起来不太可能。可能还有另一种我没有的方法不过也有想过。)
关于java - 三元运算符在 JDK8 和 JDK10 上的行为差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50769880/
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我想设置一个默认日期,例如实际日期,我该如何设置?还有如何在组合框中设置默认值顺便问一下,date_field_tag和date_field之间有什么区别? 最佳答案 试试这个:将默认日期作为第二个参数传递。youcorrectlysetthedefaultvalueofcomboboxasshowninyourquestion. 关于ruby-on-rails-date_field_tag,如何设置默认日期?[rails上的ruby],我们在StackOverflow上找到一个类似的问
请帮助我理解范围运算符...和..之间的区别,作为Ruby中使用的“触发器”。这是PragmaticProgrammersguidetoRuby中的一个示例:a=(11..20).collect{|i|(i%4==0)..(i%3==0)?i:nil}返回:[nil,12,nil,nil,nil,16,17,18,nil,20]还有:a=(11..20).collect{|i|(i%4==0)...(i%3==0)?i:nil}返回:[nil,12,13,14,15,16,17,18,nil,20] 最佳答案 触发器(又名f/f)是
我将我的Rails应用程序部署到OpenShift,它运行良好,但我无法在生产服务器上运行“Rails控制台”。它给了我这个错误。我该如何解决这个问题?我尝试更新rubygems,但它也给出了权限被拒绝的错误,我也无法做到。railsc错误:Warning:You'reusingRubygems1.8.24withSpring.UpgradetoatleastRubygems2.1.0andrun`gempristine--all`forbetterstartupperformance./opt/rh/ruby193/root/usr/share/rubygems/rubygems
我正在尝试从Postgresql表(table1)中获取数据,该表由另一个相关表(property)的字段(table2)过滤。在纯SQL中,我会这样编写查询:SELECT*FROMtable1JOINtable2USING(table2_id)WHEREtable2.propertyLIKE'query%'这工作正常:scope:my_scope,->(query){includes(:table2).where("table2.property":query)}但我真正需要的是使用LIKE运算符进行过滤,而不是严格相等。然而,这是行不通的:scope:my_scope,->(que
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候