使用Java 10的var类型推断的几个注意点!

不加选择地应用var可能会让代码不容易理解,因为模糊了类型这个概念,而人类是依据类型分类进行逻辑思考的,这样就使事情变得更糟,如果使用得当,var可以帮助改进良好的代码,使其更短更清晰,同时不会影响可理解性。

使用var需要通过减少混乱来改进代码,从而使更重要的信息脱颖而出。

本地类型推断功能背后的主要前提非常简单。使用新的保留类型名称'var'替换声明中的显式类型,并推断其类型。所以我们可以替换原来:

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

为:

var outputStream = new ByteArrayOutputStream();

Java现在允许动态类型了吗?绝对不

所有类型推断都在编译时发生,显式类型由编译器烘焙到字节代码中。在运行时,Java与以往一样静态。鉴于使用非常简单,本备忘单将集中在本地类型推断的最重要方面 - 它的实际用途。当您应该使用显式类型以及何时应该考虑类型推断时,它将提供指导。

由于想要编写这个备忘单,Oracle的JDK工程师Stuart Marks写了一篇完美的文章,给出了编码原理和使用类型推理的指导,我将它们浓缩成一张备忘单:

原则

1.阅读代码>编写代码

无论是花10分钟还是10天写一行代码,你几乎肯定会在未来的许多年里阅读它。如果代码清晰,简洁,并且最重要的是包含了解其目的的所有必要信息,那么代码将来只能维护和理解。目标是最大化可理解性。

2.本地推理应明确代码

尽可能多地将信息烘焙到代码中,以避免读者必须查看代码库的不同部分,以便了解正在发生的事情。这可以通过方法或变量命名。

3.代码可读性不应该依赖于IDE

IDE可以很棒。我的意思是真的很棒!它们可以使开发人员的开发更高效或更准确。代码必须易读且易于理解,而不依赖于IDE。通常,代码在IDE外部读取。或者,IDE可能会为读者提供多少信息。代码应该是自我暴露的。它应该是可以理解的,无需工具的帮助。

决定权在你

是否为变量提供显式类型或让Java编译器为自己解决问题的选择是一种权衡。一方面,你想减少杂乱,样板,仪式。另一方面,您不希望损害代码的可理解性。类型声明不是向读者传达信息的唯一方式。其他方法包括变量的名称和初始化表达式。

方法

1.选择提供有用信息的变量名称

一般来说,这是一种很好的做法,但在var的上下文中它更为重要。在var声明中,可以使用变量的名称来传达有关变量含义和用法的信息。用var替换显式类型通常应该伴随着改进变量名。有时,在其名称中对变量的类型进行编码可能很有用。例如:

List<Customer> x = dbconn.executeQuery(query);
 var custList = dbconn.executeQuery(query);

2.最小化局部变量的作用域

当变量的作用域很大时会发生此问题:这意味着变量声明与其用法之间有许多代码行。随着代码的维护,对类型的更改等可能最终会产生不同的行为。例如,从List移动​​到Set可能看起来没问题,但是您的代码是否依赖于稍后在同一范围内的排序?虽然类型总是静态设置,但使用相同接口的实现中的细微差别可能会让您失望。应该更改代码以减少局部变量的作用域,然后用var声明它们,而不是简单地避免在这些情况下使用var。请考虑以下代码:

var items = new HashSet<Item>(...);
items.add(MUST_BE_PROCESSED_LAST);
for (var item : items) { ... }

此代码现在有一个bug,因为集合没有定义的迭代顺序。但是,程序员可能会立即修复此错误,因为items变量的使用与其声明相邻。现在,假设此代码是大型方法的一部分,而items变量的作用域相应较大:

var items = new HashSet<Item>(...);
// ... 100 lines of code ...
items.add(MUST_BE_PROCESSED_LAST);
for (var item : items) { ... }

这个bug现在变得更难以追踪,因为该行试图将一个项目添加到集合的末尾并不足够接近类型声明以使该bug明显。

3.初始化程序为Reader提供足够的信息时,请考虑Var

局部变量通常用构造函数初始化。正在构造的类的名称通常作为左侧的显式类型显得累赘重复,如果类型名称很长,则使用var可以提供简洁而不会丢失信息:

ByteArrayOutputStream  outputStream  =  new  ByteArrayOutputStream();
 var  outputStream  =  new  ByteArrayOutputStream();

4.使用Var分解具有局部变量的链接或嵌套表达式

看看采用字符串集合并查找最常出现的字符串的代码。这可能如下所示:

return strings.stream()
              .collect(groupingBy(s -> s, counting()))
              .entrySet()
              .stream()
              .max(Map.Entry.comparingByValue())
              .map(Map.Entry::getKey);

此代码是正确的,但在多个语句中更易读。拆分语句的问题如下所示:

Map<String, Long> freqMap = strings.stream()
                                   .collect(groupingBy(s -> s, counting()));
Optional<Map.Entry<String, Long>> maxEntryOpt = freqMap.entrySet()
                                                       .stream()
                                                   .max(Map.Entry.comparingByValue());
return maxEntryOpt.map(Map.Entry::getKey);

但是原作者可能拒绝这样做,因为显式打字看起来非常混乱,分散了重要的代码。使用var允许我们更自然地表达代码,而无需支付明确声明中间变量类型的成本:

var freqMap = strings.stream()
                     .collect(groupingBy(s -> s, counting()));
var maxEntryOpt = freqMap.entrySet()
                         .stream()
                         .max(Map.Entry.comparingByValue());
return maxEntryOpt.map(Map.Entry::getKey);

有人可能合法地更喜欢第一个片段及其单个长链方法调用。但是,在某些情况下,最好分解长方法链。

5.不要担心使用局部变量导致“编程接口”太多

Java编程中常见的习惯用法是构造具体类型的实例,但要将其分配给接口类型的变量。例如:

List<String> list = new ArrayList<>();

但是,如果使用var,则推断出具体类型而不是接口:

// Inferred type of list is ArrayList<String>.
 var list = new ArrayList<String>();

使用list变量的代码现在可以形成对具体实现的依赖性。如果变量的初始化程序将来要更改,这可能会导致其推断类型发生更改,从而导致在使用该变量的后续代码中发生错误或错误。

当遵守准则2时这不是问题,因为局部变量的范围很小,可能影响后续代码的具体实现的“泄漏”的风险是有限的。

6.使用泛型时要小心

var和泛类型功能允许您在可以从已存在的信息派生时省略显式类型信息。但是,如果一起使用,它们可能最终会省略编译器正确缩小您希望推断的类型所需的所有有用信息。

PriorityQueue<Item> itemQueue = new PriorityQueue<Item>();
PriorityQueue<Item> itemQueue = new PriorityQueue<>();
 var itemQueue = new PriorityQueue<Item>();

//危险:推断为PriorityQueue <Object>
 var itemQueue = new PriorityQueue<>();

泛型方法也成功地使用了类型推断,程序员很少提供显式类型参数。如果没有提供足够类型信息的实际方法参数,则泛型方法的推断依赖于目标类型。在var声明中,没有目标类型,因此可能会出现与diamond类似的问题。例如:

// DANGEROUS: infers as List<Object>
 var list = List.of();

使用泛型方法时,可以通过构造函数或方法的实际参数提供其他类型的信息,从而允许推断出预期的类型。这确实增加了额外的间接级别,但仍然是可预测的。从而:

// OK: itemQueue infers as PriorityQueue<String>
Comparator<String> comp = ... ;
 var itemQueue = new PriorityQueue<>(comp);

7.使用Var与文字Literals时要小心

使用带文字的var不太可能提供许多优点,因为类型名称通常很短。但是,var有时很有用,例如,对齐变量名称。

布尔值,字符,长字符串和字符串等文字没有问题。从这些文字推断出的类型是精确的,因此,var的含义是明确的。当初始值设定项是数值时,尤其是整数文字时,应特别小心。如果左侧有显式类型,则数值可以静默加宽或缩小为int以外的类型。对于var,该值将被推断为int,这可能是无意的。

// ORIGINAL
boolean ready = true;
char ch = '\ufffd';
long sum = 0L;
String label = "wombat";
byte flags = 0;
short mask = 0x7fff;
long base = 17;
 var ready = true;
 var ch    = '\ufffd';
 var sum   = 0L;
 var label = "wombat";
//危险:全部推断为int
 var flags = 0;
 var mask = 0x7fff;
 var base = 17;

写在最后

点关注,不迷路;【Java_苏先生】持续更新Java相关技术及咨询文章

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,440评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,814评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,427评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,710评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,625评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,014评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,511评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,162评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,311评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,262评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,278评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,989评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,583评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,664评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,904评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,274评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,856评论 2 339

推荐阅读更多精彩内容