Java 10新特性:型別推斷

霧花_小路發表於2019-01-19

0x01 Java 10簡介

  • 自從有了校內的下載網站,很少上Oracle官網下載JDK了,結果前兩天聽鍾神說Java 10都出來了2333。幹IT這行還真是要與時俱進啊,那就來看一下Java 10吧。
  • Java 10實際上並未引入太多新特性,不過有一個新特性最引人注目:
JEP 286: Local-Variable Type Inference
  • 等了這麼久Java終於支援型別推斷了,那麼今天就來看一看Java 10的型別推斷,和其他語言相比有哪些異同吧。

0x02 型別推斷概述

  • 型別推斷是新型的高階語言提供的一類功能,允許根據編譯上下文來推斷變數的型別,不需要自己手動寫型別,使得程式碼更加簡潔。
  • 目前我接觸的程式語言中,JavaScript、Swift和Python都支援這種語法。本次Java 10更新也支援了型別推斷,對於Java這種重量級語言來說還是一件值得高興的事。

0x03 體驗Java 10的型別推斷功能

  • 讓我們寫一段Base64編碼的程式碼,體驗一下Java 10的型別推斷功能:
//Java程式碼
import java.util.Base64;
class Untitled {
    public static void main(String[] args) {
        var b64encoder = Base64.getEncoder();
        var encodeString = b64encoder.encodeToString("Hello World".getBytes());
        System.out.println(encodeString);
    }
}
  • 初步體驗還是不錯的,只是我的IDE還沒有升級,還不支援自動提示這種語法。甚至Eclipse還不能正常編譯Java 10的程式碼,我只能手動通過javac來編譯。整體而言Java 10的型別推斷功能是類似於Swift/JavaScript的,需要寫var關鍵字,並不像是Python那樣的使用方式。

0x04 Java 10型別推斷的不足

  • 上面的用法看起來很友好,那麼有沒有更自由的寫法呢?很快的我就收到了編譯器錯誤:
//Java程式碼
import java.util.Base64;
class Untitled {
    public static void main(String[] args) {
        var b64encoder = Base64.getEncoder();
        var encodeString = b64encoder.encodeToString("Hello World".getBytes());
        System.out.println(encodeString);
        var a = 1,b = 2;
    }
}
錯誤: `var` 不允許在複合宣告中使用
        var a = 1,b = 2;
            ^
1 個錯誤
  • 看來Java 10的型別推斷還是有諸多限制和不便,不像其他語言那般好用,對比一下Swift語言:
//Swift程式碼
import Foundation
let string = "Hello World"
let data = string.data(using: String.Encoding.utf8)!
let encodeString = data.base64EncodedString()
print(encodeString)
var a = 1, b = 2;
  • 比如說Java 10並沒有let關鍵字,也就是說,並不能快速的使用型別推斷定義常量。同時也不能一次用var定義多個變數,當同型別變數較多的時候,我覺得還不如把型別寫出來。
  • 同時,根據官方的說明,你也不能將var用於成員變數,只能用於區域性變數,例如下面的例子會出現編譯錯誤:
//Java程式碼
import java.util.Base64;
class Untitled {
    class Student {
        var name = "";
        Student(String name) {
            this.name = name;
        }
    }
    
    public static void main(String[] args) {
        var b64encoder = Base64.getEncoder();
        var encodeString = b64encoder.encodeToString("Hello World".getBytes());
        System.out.println(encodeString);
    }
}
錯誤: 此處不允許使用 `var`
        var name = "";
        ^
1 個錯誤
  • 而在其他語言中,你可以更自由的使用var,在任何你想要的地方,只要不引起歧義:
//Swift程式碼
import Foundation
class Student {
    var name = "";
    init(name: String) {
        self.name = name;
    }
}
var string = "Hello World"
let data = string.data(using: String.Encoding.utf8)!
let encodeString = data.base64EncodedString()
print(encodeString)
  • 作為對比,Swift比Java 10在型別推斷方面更加靈活,同時二者也有共同點——它們都是強型別語言,任何變數必須具有某種型別,所謂的型別推斷只是一種語法上的精簡。例如你不能像Python一樣在形參列表中也不使用引數型別,或者直接省略返回值型別,這些型別還都是必須的:
#Python程式碼
def printNumber(num):
    print(num)
    return 1

retCode = printNumber(4)
print(retCode)
//Swift程式碼
func printNumber(num: Int) -> Int {
    print(num)
    return 1
}
let retCode = printNumber(num: 2)
print(retCode)
//Java程式碼
import java.util.Base64;
class Untitled {
    public static int printNumber(int num) {
        System.out.println(num);
        return 1;
    }
    public static void main(String[] args) {
        var retCode = printNumber(3);
        System.out.println(retCode);
    }
}

0x05 總結

  • 經過簡單的體驗,基本清楚了Java 10的型別推斷功能。以後在區域性範圍定義物件,可以有了更簡略的寫法:
//Before Java 9
MessageDigest md = MessageDigest.getInstance("SHA-512");
//Java 10
var md = MessageDigest.getInstance("SHA-512");
  • 顯然這種程式碼不相容早期版本的Java,即使你將Java 10的程式碼編譯為位元組碼,也不能在低版本的JVM上執行。這種新語法也不能用於Android開發等用途。比如我使用OpenJDK 1.8來測試我們前面編碼base64的程式碼,就出現了異常:
$ java -version
java version "10" 2018-03-20
Java(TM) SE Runtime Environment 18.3 (build 10+46)
Java HotSpot(TM) 64-Bit Server VM 18.3 (build 10+46, mixed mode)
$ java Main
SGVsbG8gV29ybGQ=
# java -version
openjdk version "1.8.0_111"
OpenJDK Runtime Environment (IcedTea 3.2.0) (suse-33.1-x86_64)
OpenJDK 64-Bit Server VM (build 25.111-b14, mixed mode)
# java Main
Error: A JNI error has occurred, please check your installation and try again
Exception in thread "main" java.lang.UnsupportedClassVersionError: Main has been compiled by a more recent version of the Java Runtime (class file version 54.0), this version of the Java Runtime only recognizes class file versions up to 52.0
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:763)
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142)
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:467)
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:368)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:362)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:361)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
    at sun.launcher.LauncherHelper.checkAndLoadMain(LauncherHelper.java:495)
  • 所以說這種新語法還是不夠靈活,同時相容性也堪憂,但是聊勝於無。同時也會一定程度上減少Java程式碼的長度,讓程式設計更加優雅一些。同學們如果想體驗一下新語法,可以升級到Java 10,不過好多Java應用都不相容,所以升級還是需慎重啊!

相關文章