我目前正在使用JSF作为视图技术,使用JPA作为持久层的企业应用程序。 它可能是支持bean或服务方法中的某种东西,但令我震惊:是否有充分的理由在企业应用程序中使用原语?
当我开始围绕J2SE 1.2使用Java进行编程(或者是J2SE 5.0,或者也许是Java 2?)时,我仅在代码中使用了原语。 当时,我认为可以依靠默认值,而不必在使用之前初始化某些东西,这很好。 当时我对包装器并不了解太多,并且认为我没有充分的理由进一步研究它们。
我没有什么清楚的记忆,我何时开始使用原始包装器,但是我确实记得JDK 1.5何时允许在原始体及其各自的原始包装器类之间进行自动转换,或者通常称为自动装箱/拆箱。 很好,因为将int传递给需要Integer的方法并不重要,反之亦然。 语法糖似乎使编程容易一些。
构架
即使诸如JSF和JPA之类的Java企业框架为您做了很多工作,它也暴露了依赖自动装箱和原语的一些弱点。
这些框架依赖于原始包装类的使用,而不是各个原始包装类的使用。 如果使用JPA工具基于现有表创建实体,则该工具将使用变量类型的包装器。 JSF输入和转换器对对象进行操作。 使用JPA,必须这样做。 还记得原始的默认值吗? 基元的默认值可以与数据库中存储的值混淆。
如果将long用作简单的实体ID,则该实体的默认值为0L。 从技术上讲0L是一个值,因此很难知道该实体是否已保留。 如果id的类型为Long ,则很容易判断该实体是否已保留,因为id现在可以为null ,因此表示未持久的实体。
//id is long and entity is not persisted
long id = entity.getId(); //value of 0L
Long id = entity.getId(); //doesn’t help, still 0L//id is Long and entity is not persisted
Long id = entity.getId(); //null
long id = entity.getId(); //what happens here?
//reference: https://keyholesoftware.com/2014/10/13/java-and-the-sweet-science/
类型上下文
原始类型不能充分表达值的语义上下文。 假设我们从服务中检索今天的温度,并且返回的值表示度数是int 。 容易确定int度的上下文是什么?
首先,该值是以摄氏度还是华氏度表示的? 第二,如果值为0,该怎么办? 您将不得不假设该值是实际设置的,而不是默认值0。您可能想知道为什么当服务说它为0时,它感觉像是70度外。
最好的解决方案可能是使用解析为适当值的自定义对象,而不是使用原始或什至原始包装器。 对于代表温度的值,我们可以创建一个华氏温度对象。 该对象将消除对值上下文的所有怀疑。
//
public class Fahrenheit {
…private Integer degrees;
…
}Fahrenheit f = temperatureService.getDegrees(today);//
当该方法期望华氏温度时,开发人员将无法意外传入一个摄氏温度对象。
//
…
public Celsius convertDegrees(Fahrenheit f) {
…
}//only this would work
Celsius c = temperatureService.convertDegrees(f);//
使用原语作为返回值也可能是模棱两可的。 返回一个布尔值以表示某件事是否有效不一定表示发生了什么。 通过包含更多信息,使用Result对象更具描述性。
嘲笑
尽管模拟框架可以处理基元,但是模拟框架喜欢与对象一起使用。 下面的代码段是JMock中的Expectations类。 如下面的代码片段所示,一些自动装箱魔术使框架可以设置期望返回int值为6的期望。
Expectations.returnValue(Object result): context.checking(new Expectations() {{atLeast(1).of(serviceMock).getPrimitive(5);will(returnValue(6));
….
}
由于动态创建了数组对象,因此JMock似乎可以正确处理原始数组。
Expectations.returnValue(Object result): context.checking(new Expectations() {{int[] ints = { 2, 3 };atLeast(1).of(serviceMock).getPrimitives(5);will(returnValue(ints));
….
}
即使基元和基元数组与模拟框架一起使用,也感觉您未在使用真实值。 您的参数是一个int ,但是模拟框架希望您使用Integer。
思想
- 代码项目
回顾原始包装器的历史,随着时间的推移包装器的功能不断增强,自动装箱/拆箱以及从其他语言的开发人员那里阅读文章,感觉原始器可能已被排除在Java的最初实现之外。 Java是一种面向对象的语言,但是包含非对象原语。 自动装箱功能比真正的解决方案更像创可贴。
由于企业应用程序可以访问大量资源,因此我将其特定于企业应用程序。 该环境使创建使用诸如Integer和Fahrenheit之类的对象的描述性代码变得更加容易。 在垃圾收集成本和堆大小很重要的受限设备中,原语很有意义,并且比对象同类具有优势。
翻译自: https://www.javacodegeeks.com/2015/02/do-primitives-need-to-go.html