日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

matlab中float类型的_Java局部变量类型推断(Var类型)的26条细则

發(fā)布時間:2025/3/15 java 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 matlab中float类型的_Java局部变量类型推断(Var类型)的26条细则 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

原文鏈接:https://dzone.com/articles/var-work-in-progress

作者:Anghel Leonard

譯者:沈歌

Java局部變量類型推斷(LVTI),簡稱 var類型(標識符 var不是一個關鍵字,是一個預留類型名),Java 10中通過JEP 286: Local-Variable Type Inference 添加進來。作為100%編譯特征,它不會影響字節(jié)碼,運行時或者性能。在編譯時,編譯器會檢查賦值語句右側代碼,從而推斷出具體類型。它查看聲明的右側,如果這是一個初始化語句,它會用那個類型取代 var。另外,它非常有助于減少冗余代碼和樣板代碼。它還只在旨在編寫代碼時所涉及的儀式。例如,使用 varevenAndOdd=... 代替 Map<Boolean,List<Integer>>evenAndOdd... 非常方便。根據(jù)用例,它有一個代碼可讀性的權衡,會在下面第一條中提到。

此外,這里有26條細則,覆蓋了 var類型的用例,包括它的限制。

1. 爭取起有意義的局部變量名

通常我們在起全局變量名的時候會注意這一點,但是選擇局部變量名的時候不太注意。尤其是當方法很短,方法名和實現(xiàn)都不錯的時候,我們趨向于簡化我們的變量名。但是當我們使用 var替代顯式類型的時候,具體的類型是通過編譯器推斷出來的。所以,對于人來說閱讀或者理解代碼非常困難。在這一點上 var削弱了代碼可讀性。這種事情之所以會發(fā)生,是因為大多數(shù)情況下,我們會把變量類型當成是第一信息,而把變量名當成第二信息。但是使用 var的時候,恰恰相反。

示例1

即使到這里,一些朋友仍然堅持局部變量名短點好。我們看一下:

// HAVING

public boolean callDocumentationTask() {

? ?DocumentationTool dtl = ToolProvider.getSystemDocumentationTool();

? ?DocumentationTask dtt = dtl.getTask(...);

? ?return dtt.call();

}

我們換成 var時,避免:

// AVOID

public boolean callDocumentationTask() {

? ?var dtl = ToolProvider.getSystemDocumentationTool();

? ?var dtt = dtl.getTask(...);

? ?return dtt.call();

}

更好:

// PREFER

public boolean callDocumentationTask() {

? ?var documentationTool = ToolProvider.getSystemDocumentationTool();

? ?var documentationTask = documentationTool.getTask(...);

?return documentationTask.call();

}

示例2:

避免:

// AVOID

public List<Product> fetchProducts(long userId) {

? ?var u = userRepository.findById(userId);

? ?var p = u.getCart();

? ?return p;

}

更好:

// PREFER

public List<Product> fetchProducts(long userId) {

? ?var user = userRepository.findById(userId);

? ?var productList = user.getCart();

? ?return productList;

}

示例3:

爭取為局部變量起有意義的名字并不意味著要掉入過度命名的坑,避免在短方法中使用單一類型的數(shù)據(jù)流:

// AVOID

var byteArrayOutputStream = new ByteArrayOutputStream();

用如下代碼代替更加清晰:

// PREFER

var outputStream = new ByteArrayOutputStream();

// or

var outputStreamOfFoo = new ByteArrayOutputStream();

另外,你知道嗎,Java內(nèi)部使用了一個類名字叫: InternalFrameInternalFrameTitlePaneInternalFrameTitlePaneMaximizeButtonWindowNotFocusedState

額。。。命名這個類型的變量是個挑戰(zhàn)。

2. 使用數(shù)據(jù)類型標志來幫助var去推斷出預期的基本數(shù)據(jù)類型(int, long, float, double)

如果在基本數(shù)據(jù)類型中不使用有效的數(shù)據(jù)類型標志,我們可能會發(fā)現(xiàn)預期的類型和推測出的類型不一致。這是由于 var的隱式類型轉換導致的。

例如,下面兩行代碼的表現(xiàn)是符合預期的,首先,我們聲明一個 boolean 和一個 char使用顯式類型:

boolean flag = true; // 這是一個boolean類型

char a = 'a'; ? ? ? ?// 這是一個char類型

現(xiàn)在,我們使用 var代替顯式基本類型:

var flag = true; // 被推斷為boolean類型

var a = 'a'; ? ? // 被推斷為char類型

到目前為止,一切都很完美。接下來,我們看一下相同邏輯下的 int, long, double 和 float:

int intNumber = 20; ? ? ? // 這是int類型

long longNumber = 20; ? ? // 這是long類型

float floatNumber = 20; ? // 這是float類型, 20.0

double doubleNumber = 20; // 這是double類型, 20.0

以上代碼是很常見而且清晰的,現(xiàn)在我們使用 var:

避免:

// AVOID

var intNumber = 20; ? ?// 推斷為int

var longNumber = 20; ? // 推斷為int

var floatNumber = 20; ?// 推斷為int

var doubleNumber = 20; // 推斷為int

四個變量都被推斷成了 int。為了修正這個行為,我們需要依賴Java中的數(shù)據(jù)類型標志。

更好實現(xiàn):

// PREFER

var intNumber = 20; ? ? // 推斷為int

var longNumber = 20L; ? // 推斷為long

var floatNumber = 20F; ?// 推斷為float, 20.0

var doubleNumber = 20D; // 推斷為double, 20.0

但是如果我們使用小數(shù)聲明一個數(shù)字,會發(fā)生什么呢?當你認為你的數(shù)字是一個 float的時候,避免這樣做:

// 避免,如果這是一個float

var floatNumber = 20.5; // 推斷為double

你應該用對應的數(shù)據(jù)類型標志來避免這樣的問題:

// 更好, 如果這是一個float

var floatNumber = 20.5F; // 推斷為float

3. 在某些情況下,Var隱式類型轉換可以維持可維護性

在某些情況下,Var和隱式類型轉換可以維持可維護性。例如,假設我們的代碼包含兩個方法:第一個方法接收一個包含不同條目的購物卡,比較市場中不同的價格,計算出最好的價格,并匯總返回 float類型的總價。另一個方法簡單的把這個 float價格從卡中扣除。

首先,我們看一下計算最好價格的方法:

public float computeBestPrice(String[] items) {

? ...

? float price = ...;

? return price;

}

然后,我們看一下扣款的方法:

public boolean debitCard(float amount, ...) {

? ?...

}

現(xiàn)在,我們把這兩個方法匯總,提供一個服務方法。顧客選擇要買的商品,計算最優(yōu)價格,然后扣款:

// AVOID

public void purchaseCart(long customerId) {

? ?...

? ?float price = computeBestPrice(...);

? ?debitCard(price, ...);

}

一段時間后,公司想要去除價格中的小數(shù)部分作為打折策略,使用 int代替了 float, 我們需要修改代碼。

public int computeBestPrice(String[] items) {

? ...

? float realprice = ...;

? ...

? int price = (int) realprice;

? return price;

}

public boolean debitCard(int amount, ...) {

? ?...

}

問題在于我們使用了顯示類型 float,這樣的更改不能被兼容。代碼會報編譯時錯誤。但是如果我們預判到這種情況,使用 var代替 float, 我們的代碼會因為隱式類型轉換而變得沒有兼容性問題。

// PREFER

public void purchaseCart(long customerId) {

? ?...

? ?var price = computeBestPrice(...);

? ?debitCard(price, ...);

}

4. 當數(shù)據(jù)類型標志解決不了問題的時候,依賴顯式向下轉換或者避免var

一些Java基礎數(shù)據(jù)類型不支持數(shù)據(jù)類型標志。例如 byte和 short。使用顯式基礎數(shù)據(jù)類型時沒有任何問題。使用 var代替的時候:

// 這樣更好,而不是使用var

byte byteNumber = 45; ? ? // 這是byte類型

short shortNumber = 4533; // 這是short類型

為什么在這種情況下顯式類型比 var好呢?我們切換到 var.注意示例中都會被推斷為 int, 而不是我們預期的類型。

避免使用以下代碼:

// AVOID

var byteNumber = 45; ? ?// 推斷為int

var shortNumber = 4533; // 推斷為int

這里沒有基礎數(shù)據(jù)類型幫助我們,因此我們需要依賴顯示強制類型轉換。從個人角度來講,我會避免這么用,因為沒啥好處,但是可以這么用。

如果你真的想用 var,這么用:

// 如果你真的想用var,這么寫

var byteNumber = (byte) 45; ? ? // 推斷為byte

var shortNumber = (short) 4533; // 推斷為short

5. 如果變量名沒有對人來說足夠的類型信息,避免使用var

使用 var有助于提供更加簡練的代碼。例如, 在使用構造方法時(這是使用局部變量的常見示例),我們可以簡單地避免重復類名的必要性,從而消除冗余。

避免:

// AVOID

MemoryCacheImageInputStream inputStream = new MemoryCacheImageInputStream(...);

更好:

// PREFER

var inputStream = new MemoryCacheImageInputStream(...);

在下面的結構中, var也是一個簡化代碼而不丟失信息的好方法。

避免:

// AVOID

JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

StandardJavaFileManager fm = compiler.getStandardFileManager(...);

更好:

// PREFER

var compiler = ToolProvider.getSystemJavaCompiler();

var fileManager = compiler.getStandardFileManager(...);

為什么這樣基于 var的例子我們感覺比較舒服呢?因為需要的信息已經(jīng)在變量名中了。但是如果使用 var 加上變量名,還是會丟失信息,那么最好避免使用 var。

避免:

// AVOID

public File fetchCartContent() {

? ?return new File(...);

}

// As a human, is hard to infer the "cart" type without

// inspecting the fetchCartContent() method

var cart = fetchCartContent();

使用以下代碼代替:

// PREFER

public File fetchCartContent() {

? ?return new File(...);

}

File cart = fetchCartContent();

思考一個基于 java.nio.channels.Selector的例子。這個類有一個靜態(tài)方法叫做 open(),返回一個新的 Selector實例并且執(zhí)行open動作。但是 Selector.open()很容易被認為返回一個 boolean標識打開當前選擇器是否成功,或者返回 void。使用 var導致丟失信息會引發(fā)這樣的困擾。

6.?var類型確保編譯時安全

var類型是編譯時安全的。這意味著如果我們試圖實現(xiàn)一個錯的賦值,會導致編譯時報錯。例如,以下代碼編譯不會通過。

// 編譯通不過

var items = 10;

items = "10 items"; // 不兼容類型: String不能轉為int

以下代碼編譯會通過

var items = 10;

items = 20;

這個代碼也會編譯通過:

var items = "10";

items = "10 items" ;

所以,一旦編譯器已經(jīng)推斷出了 var對應的類型,我們只能賦值對應類型的值給它。

7.?var?不能被用于將真實類型的實例賦值給接口類型變量。

在Java中,我們使用“面向接口編程”的技術。

例如,我們創(chuàng)建一個 ArrayList的實例,如下(綁定代碼到抽象):

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

我們避免這樣的事情(綁定代碼到實現(xiàn)):

ArrayList<String> products = new ArrayList<>();

所以,通過第一個例子創(chuàng)建一個 ArrayList實例更好,但是我們也需要聲明一個 List類型的變量。因為 List是一個接口,我們可以很容易的切換到 List的其他實現(xiàn)類,而無需額外的修改。

這就是“面向接口編程”,但是 var不能這么用。這意味著當我們使用 var時,推斷出的類型是實現(xiàn)類的類型。例如,下面這行代碼,推測出的類型是 ArrayList<String>:

var productList = new ArrayList<String>(); // 推斷為ArrayList

以下幾個論點支持這一行為:

  • 首先,?var是局部變量,大多數(shù)情況下,“面向接口編程”在方法參數(shù)和返回類型的時候更有用。

  • 局部變量的作用域比較小,切換實現(xiàn)引起的發(fā)現(xiàn)和修復成本比較低。

  • var將其右側的代碼視為用于對端實際類型的初始化程序,如果將來修改初始化程序,則推斷類型會改變,從而導致后續(xù)依賴此變量的代碼產(chǎn)生問題。

8. 意外推斷類型的可能性

如果不存在推斷類型所需的信息,則與菱形運算符組合的 var類型可能導致意外推斷類型。

在Java 7之前的Coin項目中,我們寫了這樣的代碼:

//顯式指定泛型類的實例化參數(shù)類型

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

從Java 7開始,我們有了菱形運算符,它能夠推斷泛型類實例化參數(shù)類型:

// inferring generic class's instantiation parameter type

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

那么,以下代碼推斷出什么類型呢?

首先應該避免這么用:

// AVOID

var productList = new ArrayList<>(); // 推斷為ArrayList

推斷出的類型是Object的ArrayList。之所以會這樣是因為沒有找到能夠推測到預期類型為String的信息,這會導致返回一個最廣泛可用類型,Object。

所以為了避免這樣的情形,我們必須提供能夠推斷到預測類型的信息。這個可以直接給也可以間接給。

更好的實現(xiàn)(直接):

// PREFER

var productList = new ArrayList<String>(); // 推斷為ArrayList

更好的實現(xiàn)(間接):

var productStack = new ArrayDeque<String>();

var productList = new ArrayList<>(productStack); // 推斷為ArrayList

更好的實現(xiàn)(間接):

Product p1 = new Product();

Product p2 = new Product();

var listOfProduct = List.of(p1, p2); // 推斷為List

// 不要這么干

var listofProduct = List.of(); // 推斷為List

listofProduct.add(p1);

listofProduct.add(p2);

9. 賦值數(shù)組到var不需要中括號[]

我們都知道Java中如何聲明一個數(shù)組:

int[] numbers = new int[5];

// 或者,這樣寫不太好

int numbers[] = new int[5];

那么怎么用var呢?左邊不需要使用括號。

避免這么寫(編譯不通過):

// 編譯通不過

var[] numbers = new int[5];

// 或者

var numbers[] = new int[5];

應該這么用:

// PREFER

var numbers = new int[5]; // 推斷為int數(shù)組

numbers[0] = 2; ? // 對

numbers[0] = 2.2; // 錯

numbers[0] = "2"; // 錯

另外,這么用也不能編譯,這是因為右邊沒有自己的類型。

// 顯式類型表現(xiàn)符合預期

int[] numbers = {1, 2, 3};

// 編譯通不過

var numbers = {1, 2, 3};

var numbers[] = {1, 2, 3};

var[] numbers = {1, 2, 3};

10.?var類型不能被用于復合聲明(一行聲明多個變量)

如果你是復合聲明的粉絲,你一定要知道 var不支持這種聲明。下面的代碼不能編譯:

// 編譯通不過

// error: 'var' 不允許復合聲明

var hello = "hello", bye = "bye", welcome = "welcome";

用下面的代碼代替:

// PREFER

String hello = "hello", bye = "bye", welcome = "welcome";

或者這么用:

// PREFER

var hello = "hello";

var bye = "bye";

var welcome = "welcome";

11. 局部變量應力求最小化其范圍。var類型強化了這一論點。

局部變量應該保持小作用域,我確定你在 var出現(xiàn)之前就聽過這個,這樣可以增強代碼可讀性,也方便更快的修復bug。

例如我們定義一個java棧:

避免:

// AVOID

...

var stack = new Stack<String>();

stack.push("George");

stack.push("Tyllen");

stack.push("Martin");

stack.push("Kelly");

...

// 50行不用stack的代碼

// George, Tyllen, Martin, Kelly ?

stack.forEach(...);

...

注意我們調用 forEach 方法,該方法繼承自 java.util.Vector.這個方法將以Vector的方式遍歷?!,F(xiàn)在我們準備切換 Stack到 ArrayDeque,切換之后 forEach()方法將變成 ArrayDeque的,將以stack(LIFO)的方式遍歷stack。

// AVOID

...

var stack = new ArrayDeque<String>();

stack.push("George");

stack.push("Tyllen");

stack.push("Martin");

stack.push("Kelly");

...

// 50行不用stack的代碼

// Kelly, Martin, Tyllen, George

stack.forEach(...);

...

這不是我們想要的,我們很難看出引入了一個錯誤,因為包含 forEach()部分的代碼不在研發(fā)完成修改的代碼附近。為了快速修復這個錯誤,并避免上下滾動來了解發(fā)生了什么,最好縮小stack變量的作用域范圍。

最好這么寫:

// PREFER

...

var stack = new Stack<String>();

stack.push("George");

stack.push("Tyllen");

stack.push("Martin");

stack.push("Kelly");

...

// George, Tyllen, Martin, Kelly ?

stack.forEach(...);

...

// 50行不用stack的代碼

現(xiàn)在,當開發(fā)人員從 Stack切換到 ArrayQueue的時候,他們能夠很快的注意到bug,并修復它。

12.?var類型便于三元運算符右側的不同類型的操作數(shù)

我們可以在三元運算符的右側使用不同類型的操作數(shù)。

使用具體類型的時候,以下代碼無法編譯:

// 編譯通不過

List code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10);

// or

Set code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10);

雖然我們可以這么寫:

Collection code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10);

Object code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10);

這樣也編譯不過:

// 編譯通不過:

int code = intOrString ? 12112 : "12112";

String code = intOrString ? 12112 : "12112";

但是我們可以這么寫:

Serializable code = intOrString ? 12112 : "12112";

Object code = intOrString ? 12112 : "12112";

在這種情況下,使用 var更好:

// PREFER

// inferred as Collection

var code = containsDuplicates ? List.of(12, 1, 12) : Set.of(12, 1, 10);

// inferred as Serializable

var code = intOrString ? 12112 : "12112";

千萬不要從這些例子中得出var類型是在運行時做類型推斷的,它不是!!!

當然,我們使用相同的類型作為操作數(shù)時 var是支持的。

// 推斷為float

var code = oneOrTwoDigits ? 1211.2f : 1211.25f;

13.?var類型能夠用在循環(huán)體中

我們能非常簡單的在for循環(huán)中用 var類型取代具體類型。這是兩個例子。

var替換int:

// 顯式類型

for (int i = 0; i < 5; i++) {

? ? ...

}

// 使用 var

for (var i = 0; i < 5; i++) { // i 推斷為 int類型

? ? ...

}

var替換Order:

List<Order> orderList = ...;

// 顯式類型

for (Order order : orderList) {

? ?...

}

// 使用 var

for (var order : orderList) { // order 推斷成Order類型

? ?...

}

14.?var類型能夠和Java 8中的Stream一起用

將Java10中的var與Java 8中的Stream結合起來非常簡單。

你需要使用var取代顯式類型Stream:

例1:

// 顯式類型

Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 5); ? ? ? ? ? ? ? ?

numbers.filter(t -> t % 2 == 0).forEach(System.out::println);

// 使用 var

var numbers = Stream.of(1, 2, 3, 4, 5); // 推斷為 Stream ? ? ? ? ? ? ?

numbers.filter(t -> t % 2 == 0).forEach(System.out::println);

例2:

// 顯式類型

Stream<String> paths = Files.lines(Path.of("..."));

List<File> files = paths.map(p -> new File(p)).collect(toList());

// 使用 var

var paths = Files.lines(Path.of("...")); // 推斷為 Stream

var files = paths.map(p -> new File(p)).collect(toList()); // 推斷為 List

15.?var類型可用于聲明局部變量,可用于分解表達式嵌套/長鏈

var類型可用于聲明局部變量,可用于分解表達式嵌套/長鏈.

大的或者嵌套的表達看起來令人印象深刻,通常它們被認為是聰明的代碼。有時候我們會故意這么寫,有時候我們從一個小表達式開始寫,慢慢越來越大。為了提高代碼可讀性,建議用局部變量來破壞大型/嵌套表達式。但有時候,添加這些局部變量是我們想要避免的體力活。如下:

避免:

List<Integer> intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5);

// AVOID

int result = intList.stream()

? ?.collect(Collectors.partitioningBy(i -> i % 2 == 0))

? ?.values()

? ?.stream()

? ?.max(Comparator.comparing(List::size))

? ?.orElse(Collections.emptyList())

? ?.stream()

? ?.mapToInt(Integer::intValue)

? ?.sum();

更好:

List<Integer> intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5);

// PREFER

Map<Boolean, List<Integer>> evenAndOdd = intList.stream()

? ?.collect(Collectors.partitioningBy(i -> i % 2 == 0));

Optional<List<Integer>> evenOrOdd = evenAndOdd.values()

? ?.stream()

? ?.max(Comparator.comparing(List::size));

int sumEvenOrOdd = evenOrOdd.orElse(Collections.emptyList())

? ?.stream()

? ?.mapToInt(Integer::intValue)

? ?.sum();

第二段代碼可讀性更強,更簡潔,但是第一段代碼也是對的。我們的思維會適應這樣的大表達式并且更喜歡它們而不是局部變量。然而,使用var類型對于使用局部變量的方式來說是一個優(yōu)化,因為它節(jié)省了獲取顯式類型的時間。

更好

var intList = List.of(1, 1, 2, 3, 4, 4, 6, 2, 1, 5, 4, 5);

// PREFER

var evenAndOdd = intList.stream()

? ?.collect(Collectors.partitioningBy(i -> i % 2 == 0));

var evenOrOdd = evenAndOdd.values()

? ?.stream()

? ?.max(Comparator.comparing(List::size));

var sumEvenOrOdd = evenOrOdd.orElse(Collections.emptyList())

? ?.stream()

? ?.mapToInt(Integer::intValue)

? ?.sum();

16.?var類型不能被用于方法返回類型或者方法參數(shù)類型。

試著寫下面的兩段代碼,編譯通不過。

使用var作為方法返回類型:

// 編譯通不過

public var countItems(Order order, long timestamp) {

? ?... ? ? ? ?

}

使用var作為方法參數(shù)類型:

// 編譯通不過

public int countItems(var order, var timestamp) {

? ?... ?

}

17.?var類型的局部變量可以用來傳入到方法參數(shù),也可以用來存放方法返回值

var類型的局部變量可以用來傳入到方法參數(shù),也可以用來存放方法返回值。下面這兩段代碼能夠編譯而且運行。

public int countItems(Order order, long timestamp) {

? ?...

}

public boolean checkOrder() {

? ?var order = ...; ? ? // Order實例

? ?var timestamp = ...; // long類型的 timestamp

? ?var itemsNr = countItems(order, timestamp); // 推斷為int類型

? ?...

}

它也適用于泛型。下面的代碼片段也是對的。

public <A, B> B contains(A container, B tocontain) {

? ?...

}

var order = ...; ? // Order實例

var product = ...; // Product實例

var resultProduct = contains(order, product); // inferred as Product type

18.?var類型能和匿名類一起使用。

避免:

public interface Weighter {

? ?int getWeight(Product product);

}

// AVOID

Weighter weighter = new Weighter() {

? ?@Override

? ?public int getWeight(Product product) {

? ? ? ?...

? ?}

};

Product product = ...; // Product實例

int weight = weighter.getWeight(product);

更好的代碼:

public interface Weighter {

? ?int getWeight(Product product);

}

// PREFER

var weighter = new Weighter() {

? ?@Override

? ?public int getWeight(Product product) {

? ? ? ?...

? ?}

};

var product = ...; // Product實例

var weight = weighter.getWeight(product);

19.?var類型可以是Effectively Final

從Java SE 8開始,局部類可以訪問封閉塊內(nèi)final或者effectively final的參數(shù)。變量初始化后不再改變的參數(shù)為effectively final。

所以,var類型的變量可以是effectively final的。我們可以從以下代碼中看到。

避免:

public interface Weighter {

? ?int getWeight(Product product);

}

// AVOID

int ratio = 5; // 這是effectively final

Weighter weighter = new Weighter() {

? ?@Override

? ?public int getWeight(Product product) {

? ? ? ?return ratio * ...;

? ?}

};

ratio = 3; // 這行賦值語句會報錯

更好:

public interface Weighter {

? ?int getWeight(Product product);

}

// PREFER

var ratio = 5; // 這是effectively final

var weighter = new Weighter() {

? ?@Override

? ?public int getWeight(Product product) {

? ? ? ?return ratio * ...;

? ?}

};

ratio = 3; // 這行賦值語句會報錯

20.?var類型可以用final修飾

默認情況下,var類型的局部變量可以被重新賦值(除非它是effectively final的)。但是我們可以聲明它為final類型,如下:

避免:

// AVOID

// IT DOESN'T COMPILE

public void discount(int price) {

? ?final int limit = 2000;

? ?final int discount = 5;

? ?if (price > limit) {

? ? ? ?discount++; // 這行會報錯

? ?}

}

更好:

// PREFER

// IT DOESN'T COMPILE

public void discount(int price) {

? ?final var limit = 2000;

? ?final var discount = 5;

? ?if (price > limit) {

? ? ? ?discount++; // 這行會報錯

? ?}

}

21. Lambda表達式和方法引用需要顯示對象類型

當對應的類型推斷不出來時不能使用var類型。所以,lambda表達式和方法引用初始化不被允許。這是var類型限制的一部分。

下面的代碼無法編譯:

// 編譯不通過

// lambda表達式需要顯式目標類型

var f = x -> x + 1;

// 方法引用需要顯式目標類型

var exception = IllegalArgumentException::new;

用以下代碼代替:

// PREFER

Function<Integer, Integer> f = x -> x + 1;

Supplier<IllegalArgumentException> exception = IllegalArgumentException::new;

但是在lambda的內(nèi)容中,Java 11允許我們?nèi)ナ褂胿ar作為lambda參數(shù)。例如,以下代碼在Java 11中可以很好的工作(詳見JEP 323(lambda參數(shù)中的局部變量))

// Java 11

(var x, var y) -> x + y

// or

(@Nonnull var x, @Nonnull var y) -> x + y

22. 為var類型賦值為null是不被允許的。

此外,也不允許缺少初始化程序。這是var類型的另一個限制。

以下代碼不會編譯通過(賦值null):

// 編譯通不過

var message = null; // 類型錯誤: 變量初始化為'null'

這個代碼也不會編譯通過(缺少初始化):

// IT DOESN'T COMPILE

var message; // 使用var不能不做初始化

...

message = "hello";

更好:

// PREFER

String message = null;

// or

String message;

...

message = "hello";

23.?var類型不能作為對象的域(Field)

var類型可以用來做局部變量,但是不能用來做對象的域/全局變量。

這個限制會導致這里的編譯錯誤:

// 編譯通不過

public class Product {

? ?private var price; // 'var' 不被允許

? ?private var name; ?// 'var' 不被允許

? ?...

}

用以下代碼代替:

// PREFER

public class Product {

? ?private int price;

? ?private String name;

? ?...

}

24.?var不被允許在catch塊中使用

但是它被允許在try-with-resources中。

catch塊

當代碼拋出異常時,我們必須通過顯式類型catch它,因為var類型不被允許。這個限制會導致以下代碼的編譯時錯誤:

// 編譯通不過

try {

? ?TimeUnit.NANOSECONDS.sleep(5000);

} catch (var ex) {

? ?...

}

用這個取代:

// PREFER

try {

? ?TimeUnit.NANOSECONDS.sleep(5000);

} catch (InterruptedException ex) {

? ?...

}

try-with-resources

另一方面,var類型可以用在try-with-resource中,例如:

// 顯式類型

try (PrintWriter writer = new PrintWriter(new File("welcome.txt"))) {

? ?writer.println("Welcome message");

}

可以用var重寫:

// 使用 var

try (var writer = new PrintWriter(new File("welcome.txt"))) {

? ?writer.println("Welcome message");

}

25.?var類型不能和泛型T一起使用

假定我們有下面的代碼:

public <T extends Number> T add(T t) {

? ? T temp = t;

? ? ...

? ? return temp; ?

}

這種情況下,使用var的運行結果是符合預期的,我們可以用var替換T,如下:

public <T extends Number> T add(T t) {

? ? var temp = t;

? ? ...

? ? return temp; ?

}

我們看一下另一個var能夠成功使用的例子,如下:

public <T extends Number> T add(T t) {

? ? List<T> numbers = new ArrayList<>();

? ? numbers.add((T) Integer.valueOf(3));

? ? numbers.add((T) Double.valueOf(3.9));

? ? numbers.add(t);

? ? numbers.add("5"); // 錯誤:類型不兼容,string不能轉為T

? ? ... ? ?

}

可以用var取代List, 如下:

public <T extends Number> T add(T t) {

? ? var numbers = new ArrayList<T>();

? ? // DON'T DO THIS, DON'T FORGET THE, T

? ? var numbers = new ArrayList<>();

? ? numbers.add((T) Integer.valueOf(3));

? ? numbers.add((T) Double.valueOf(3.9));

? ? numbers.add(t);

? ? numbers.add("5"); // // 錯誤:類型不兼容,string不能轉為T

? ? ... ? ?

}

26. 使用帶有var類型的通配符(?),協(xié)方差和反對變量時要特別注意

使用?通配符

這么做是安全的:

// 顯式類型

Class> clazz = Integer.class;

// 使用var

var clazz = Integer.class;

但是,不要因為代碼中有錯誤,而var可以讓它們魔法般的消失,就使用var取代Foo。看下一個例子,不是非常明顯,但是我想讓它指出核心。考慮一下當你編寫這一段代碼的過程,也許,你嘗試定義一個String的ArrayList,并最終定義成了Collection。

// 顯式類型

Collection> stuff = new ArrayList<>();

stuff.add("hello"); // 編譯錯誤

stuff.add("world"); // 編譯錯誤

// 使用var,錯誤會消失,但是我不確定你是你想要的結果

var stuff = new ArrayList<>();

strings.add("hello"); // 錯誤消失

strings.add("world"); // 錯誤消失

Java協(xié)變(Foo)和逆變(Foo)

我們知道可以這么寫:

// 顯式類型

Class extends Number> intNumber = Integer.class;

Class super FilterReader> fileReader = Reader.class;

而且如果我們錯誤賦值了錯誤的類型,接收到一個編譯時錯誤,這就是我們想要的:

// 編譯通不過

// 錯誤: Class 不能轉換到 Class extends Number>

Class extends Number> intNumber = Reader.class;

// 錯誤: Class 不能轉化到Class super FilterReader>

Class super FilterReader> fileReader = Integer.class;

但是如果我們使用var:

// using var

var intNumber = Integer.class;

var fileReader = Reader.class;

然后我們可以為這些變量賦值任何類,因此我們的邊界/約束消失了,這并不是我們想要的。

// 編譯通過

var intNumber = Reader.class;

var fileReader = Integer.class;

結論

var類型非常酷,而且將來會繼續(xù)升級。留心JEP 323 和 JEP 301了解更多。祝您愉快。

推薦:?SpringBoot WebFlux 入門案例

上一篇:應用程序之Kubernetes和Spring Boot自我修復

關注公眾號

點擊原文

總結

以上是生活随笔為你收集整理的matlab中float类型的_Java局部变量类型推断(Var类型)的26条细则的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。