《Java8實戰》筆記(15):面向對象和函數式編程的混合-Java 8和Scala的比較

面向對象和函數式編程的混合:Java 8和Scala的比較

Scala是一種混合了面向對象和函數式編程的語言。它常常被看作Java的一種替代語言,程序員們希望在運行于JVM上的靜態類型語言中使用函數式特性,同時又期望保持Java體驗的一致性。和Java比較起來,Scala提供了更多的特性,包括更復雜的類型系統、類型推斷、模式匹配、定義域語言的結構等。除此之外,你可以在Scala代碼中直接使用任何一個Java類庫。

Scala簡介

HelloWorld

命令式Scala

object Beer {def main(args: Array[String]){var n : Int = 2while( n <= 6 ){println(s"Hello ${n} bottles of beer")n += 1}}
}

輸出

Hello 2 bottles of beer
Hello 3 bottles of beer
Hello 4 bottles of beer
Hello 5 bottles of beer
Hello 6 bottles of beer

函數式Scala

Java 8以更加函數式的方式實現

public class Foo {public static void main(String[] args) {IntStream.rangeClosed(2, 6).forEach(n -> System.out.println("Hello " + n +" bottles of beer"));}
}

Scala來實現

object Beer {def main(args: Array[String]){2 to 6 foreach { n => println(s"Hello ${n} bottles of beer") }}
}

基礎數據結構:List、Set、Map、Tuple、Stream以及Option

創建集合

在Scala中創建集合是非常簡單的

val authorsToAge = Map("Raoul" -> 23, "Mario" -> 40, "Alan" -> 53)

Java中那樣手工添加每一個元素:

Map<String, Integer> authorsToAge = new HashMap<>();
authorsToAge.put("Raoul", 23);
authorsToAge.put("Mario", 40);
authorsToAge.put("Alan", 53);

Scala輕松地創建List(一種單向鏈表)或者Set(不帶冗余數據的集合)

val authors = List("Raoul", "Mario", "Alan")
val numbers = Set(1, 1, 2, 3, 5, 8)

Scala中,關鍵字val表明變量是只讀的,并由此不能被賦值(就像Java中聲明為final的變量一樣)。而關鍵字var表明變量是可以讀寫的。

不可變與可變的比較

Scala的集合有一個重要的特質我們應該牢記在心,那就是我們之前創建的集合在默認情況下是只讀的。這意味著它們從創建開始就不能修改。

更新一個Scala集合會生成一個新的集合

val numbers = Set(2, 5, 3);
val newNumbers = numbers + 8 //這里的操作符+會將8添加到Set中,創建并返回一個新的Set對象
println(newNumbers)
println(numbers)

Java中提供了多種方法創建不可修改的(unmodifiable)集合。下面的代碼中,變量newNumbers是集合Set對象numbers的一個只讀視圖:

Set<Integer> numbers = new HashSet<>();
Set<Integer> newNumbers = Collections.unmodifiableSet(numbers);

這意味著你無法通過操作變量newNumbers向其中加入新的元素。不過,不可修改集合僅僅是對可變集合進行了一層封裝。通過直接訪問numbers變量,你還是能向其中加入元素。

與此相反,不可變(immutable)集合確保了該集合在任何時候都不會發生變化,無論有多少個變量同時指向它。

使用集合

val fileLines = Source.fromFile("data.txt").getLines.toList()
val linesLongUpper = fileLines.filter(l => l.length() > 10).map(l => l.toUpperCase())

元組

Java目前還不支持元組

Scala提供了名為元組字面量

val raoul = ("Raoul", "+ 44 887007007")
val alan = ("Alan", "+44 883133700")

Scala支持任意大小的元組

val book = (2014, "Java 8 in Action", "Manning")
val numbers = (42, 1337, 0, 3, 14)

你可以依據它們的位置,通過存取器(accessor) _1、_2(從1開始的一個序列)訪問元組中的元素,比如:

println(book._1)
println(numbers._4)

Stream

Scala也提供了對應的數據結構,它采用延遲方式計算數據結構,名稱也叫Stream!不過Scala中的Stream提供了更加豐富的功能,讓Java中的Stream有些黯然失色。Scala中的Stream可以記錄它曾經計算出的值,所以之前的元素可以隨時進行訪問。

除此之外,Stream還進行了索引,所以Stream中的元素可以像List那樣通過索引訪問。注意,這種抉擇也附帶著開銷,由于需要存儲這些額外的屬性,和Java 8中的Stream比起來,Scala版本的Stream內存的使用效率變低了,因為Scala中的Stream需要能夠回溯之前的元素,這意味著之前訪問過的元素都需要在內存“記錄下來”(即進行緩存)。

Option

Java8的Optional

public String getCarInsuranceName(Optional<Person> person, int minAge) {return person.filter(p -> p.getAge() >= minAge).flatMap(Person::getCar).flatMap(Car::getInsurance).map(Insurance::getName).orElse("Unknown");
}

在Scala語言中,你可以使用Option使用Optional類似的方法實現該函數:

def getCarInsuranceName(person: Option[Person], minAge: Int) = person.filter(_.getAge() >= minAge).flatMap(_.getCar).flatMap(_.getInsurance).map(_.getName).getOrElse("Unknown")

函數

Scala中的一等函數

def isJavaMentioned(tweet: String) : Boolean = tweet.contains("Java")
def isShortTweet(tweet: String) : Boolean = tweet.length() < 20

Scala語言中,你可以直接傳遞這兩個方法給內嵌的filter,如下所示

val tweets = List("I love the new features in Java 8","How's it going?","An SQL query walks into a bar, sees two tables and says 'Can I join you?'"
)
tweets.filter(isJavaMentioned).foreach(println)
tweets.filter(isShortTweet).foreach(println)

現在,讓我們一起審視下內嵌方法filter的函數簽名:

def filter[T](p: (T) => Boolean): List[T]

匿名函數和閉包

匿名函數

val isLongTweet : String => Boolean= (tweet : String) => tweet.length() > 60val isLongTweet : String => Boolean= new Function1[String, Boolean] {def apply(tweet: String): Boolean = tweet.length() > 60
}isLongTweet.apply("A very short tweet")

如果用Java,你可以采用下面的方式:

Function<String, Boolean> isLongTweet = (String s) -> s.length() > 60;
boolean long = isLongTweet.apply("A very short tweet");isLongTweet("A very short tweet")

閉包

閉包是一個函數實例,它可以不受限制地訪問該函數的非本地變量。不過Java 8中的Lambda表達式自身帶有一定的限制:它們不能修改定義Lambda表達式的函數中的本地變量值。這些變量必須隱式地聲明為final。

Scala中的匿名函數可以取得自身的變量,但并非變量當前指向的變量值。

def main(args: Array[String]) {var count = 0val inc = () => count+=1inc()println(count)inc()println(count)
}

不過在Java中,下面的這段代碼會遭遇編譯錯誤,因為count隱式地被強制定義為final:

public static void main(String[] args) {int count = 0;Runnable inc = () -> count+=1;//錯誤:count必須為final或者在效果上為finalinc.run();System.out.println(count);inc.run();
}

科里化

Java的示例

static int multiply(int x, int y) {return x * y;
}
int r = multiply(2, 10);static Function<Integer, Integer> multiplyCurry(int x) {return (Integer y) -> x * y;
}Stream.of(1, 3, 5, 7).map(multiplyCurry(2)).forEach(System.out::println);

Scala提供了一種特殊的語法可以自動完成這部分工作。

def multiply(x : Int, y: Int) = x * y
val r = multiply(2, 10);

該函數的科里化版本如下:

def multiplyCurry(x :Int)(y : Int) = x * y
val r = multiplyCurry(2)(10)val multiplyByTwo : Int => Int = multiplyCurry(2)
val r = multiplyByTwo(10)

類和trait

更加簡潔的Scala類

由于Scala也是一門完全的面向對象語言,你可以創建類,并將其實例化生成對象。

class Hello {def sayThankYou(){println("Thanks for reading our book")}
}
val h = new Hello()
h.sayThankYou()

getter方法和setter方法

單純只定義字段列表的Java類,你還需要聲明一長串的getter方法、setter方法,以及恰當的構造器。多麻煩啊!

public class Student {private String name;private int id;public Student(String name) {this.name = name;}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}
}

Scala語言中構造器、getter方法以及setter方法都能隱式地生成,從而大大降低你代碼中的冗余:

class Student(var name: String, var id: Int)
val s = new Student("Raoul", 1)
println(s.name)
s.id = 1337
println(s.id)

Scala的trait與Java8的接口對比

Scala還提供了另一個非常有助于抽象對象的特性,名稱叫trait。它是Scala為實現Java中的接口而設計的替代品。trait中既可以定義抽象方法,也可以定義帶有默認實現的方法。trait同時還支持Java中接口那樣的多繼承,所以你可以將它們看成與Java 8中接口類似的特性,它們都支持默認方法。trait中還可以包含像抽象類這樣的字段,而Java 8的接口不支持這樣的特性。

trait Sized{var size : Int = 0def isEmpty() = size == 0
}class Empty extends Sized//一個繼承自trait Sized的類
println(new Empty().isEmpty())//打印輸出true

你可以創建一個Box類,動態地決定到底選擇哪一個實例支持由trait Sized定義的操作

class Box
val b1 = new Box() with Sized //在對象實例化時構建trait
println(b1.isEmpty()) //打印輸出true
val b2 = new Box()
b2.isEmpty() //編譯錯誤:因為Box類的聲明并未繼承Sized

小結

  • Java 8和Scala都是整合了面向對象編程和函數式編程特性的編程語言,它們都運行于JVM之上,在很多時候可以相互操作。
  • Scala支持對集合的抽象,支持處理的對象包括List、Set、Map、Stream、Option,這些和Java 8非常類似。不過,除此之外Scala還支持元組。
  • Scala為函數提供了更加豐富的特性,這方面比Java 8做得好,Scala支持:函數類型、可以不受限制地訪問本地變量的閉包,以及內置的科里化表單。
  • Scala中的類可以提供隱式的構造器、getter方法以及setter方法。
  • Scala還支持trait,它是一種同時包含了字段和默認方法的接口。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445869.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445869.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445869.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

函數指針作為形參進行調用

代碼 兩個代碼均位于namespace作用域之內addOne將傳遞進來的形參進行加一&#xff0c;然后返回performance_test函數主要是想簡化函數調用&#xff0c;兩個形參&#xff0c;第一個表示循環的次數&#xff0c;第二個是帶參數的函數指針&#xff0c;函數內部初始化start和end兩個…

python中fetchall_Python連接MySQL并使用fetchall()方法過濾特殊字符

來一個簡單的例子&#xff0c;看Python如何操作數據庫&#xff0c;相比Java的JDBC來說&#xff0c;確實非常簡單&#xff0c;省去了很多復雜的重復工作&#xff0c;只關心數據的獲取與操作。準備工作需要有相應的環境和模塊&#xff1a;Ubuntu 14.04 64bitPython 2.7.6MySQLdb注…

《Java8實戰》筆記(16):結論以及Java的未來

結論以及Java的未來 回顧Java8的語言特性 行為參數化&#xff08;Lambda以及方法引用&#xff09; 流 CompletableFuture Optional 默認方法 Java的未來 集合 類型系統的改進 聲明位置變量 更多的類型推斷 模式匹配 更加豐富的泛型形式 具化泛型 泛型中特別為函…

解決吉大正源(身份認證網關|USBKey)和gmssl(server|client)使用gmtl協議交叉互通報錯tlsv1 alert decrypt error

報錯內容 SSL_connect:error in SSLv3/TLS write finished140057291788288:error:1409441B:SSL routines:ssl3_read_bytes:tlsv1 alert decrypt error:ssl/record/rec_layer_s3.c:1385:SSL alert number 51 報錯原因 gmssl庫生成 certificate verify 消息時&#xff0c;對自客…

12無法使用otg_12個冷知識:或許只能看看而無法使用,但卻真實存在著

12個或許只能看看而無法使用&#xff0c;但卻真實存在著。臉紅一所有已知動物中&#xff0c;唯一可以臉紅的是人類。二有些地區將雨水歸類為公共財物&#xff0c;作為公共財物是不允許收集的&#xff0c;違反者將面臨處罰。三世界上汽車研發成本最高的一款車是福特蒙迪歐&#…

《Java8實戰》筆記匯總

《Java8實戰》筆記&#xff08;01&#xff09;&#xff1a;為什么要關心Java8 《Java8實戰》筆記&#xff08;02&#xff09;&#xff1a;通過行為參數傳遞代碼 《Java8實戰》筆記&#xff08;03&#xff09;&#xff1a;Lambda表達式 《Java8實戰》筆記&#xff08;04&…

三目運算符_C語言知識點:運算符的優先級和結合性

運算符是一種告訴編譯器執行特定的數學或邏輯操作的符號。C語言內置了豐富的運算符&#xff0c;大體可分為10類&#xff1a;算術運算符、關系運算符、邏輯運算符、位操作運算符、賦值運算符、條件運算符、逗號運算符、指針運算符、求字節數運算符和特殊運算符。根據運算符可操作…

可以直接進行運算么_WORD辦公技巧:如何直接在WORD中進行加法、乘法運算?

排版目標下圖文檔中有一張2020年&#xff11;&#xff0d;&#xff13;月口罩購買情況統計表&#xff0c;數據量并不大&#xff0c;我們想不動用excel表格進行統計&#xff0c;直接利用WORD自帶的函數公式計算出表格內空白單元格的數值。其中&#xff0c;金額&#xff1d;單價&…

對dladdr未定義的引用

參考鏈接 c - 為什么我得到“對dladdr的未定義引用”&#xff0c;即使是這個簡單程序的-ldl&#xff1f; | 碼農俱樂部 - Golang中國 - Go語言中文社區 注意事項 dladdr需要與-ldl鏈接&#xff0c;且-ldl放置在鏈接的最后 CMakeLists.txt 模板 cmake_minimum_required(VERSI…

如何優雅互換Map鍵與值

一般方法 //map must be a bijection in order for this to work properly public static <K,V> HashMap<V,K> reverse(Map<K,V> map) {HashMap<V,K> rev new HashMap<V, K>();for(Map.Entry<K,V> entry : map.entrySet())rev.put(entry…

python程序設計題怎么寫_《Python語言程序設計基礎》第1章程序練習題

本文為中國大學MOOC《Python語言程序設計》課程學習筆記&#xff0c;課程主講&#xff1a;嵩天老師&#xff0c;練習平臺&#xff1a;Python123&#xff0c;參考教材&#xff1a;《Python語言程序設計基礎》1.1 字符串拼接Str1 input("請輸入一個人的名字&#xff1a;&qu…

《劍指Offer》36:二叉搜索樹與雙向鏈表

題目 輸入一棵二叉搜索樹&#xff0c;將該二叉搜索樹轉換成一個排序的雙向鏈表。要求不能創建任何新的節點&#xff0c;只能調整樹中節點指針的指向。比如&#xff0c;輸入下圖中的二叉搜索樹&#xff0c;輸出轉換之后的排序雙向鏈表。 二叉樹節點的定義如下&#xff1a; pub…

窗口位置按鈕取消_VBA002:“宏”的保存位置有哪幾種方式?

商務合作請加微信 | Allen_Lyq文章投稿 | jiangjunpeng1996126.com微信公眾號 | Word和Excel達人先生頭條號 | 跟小小筱學辦公技能通過上一篇文章的學習&#xff0c;我們已經知道宏的基本用法&#xff0c;在錄制宏的過程中&#xff0c;還有幾個點需要我們注意下&#xff1a;宏命…

《劍指Offer》60:n個骰子的點數

題目 把n個骰子扔在地上&#xff0c;所有骰子朝上一面的點數之和為S。輸入n&#xff0c;打印出S的所有可能的值出現的概率。 分析 直接法 假設骰子有face面&#xff0c;有n個骰子&#xff0c;那么總排列數就有face?個。&#xff08;例如&#xff0c;有3個6面骰子&#xff…

fastjson解析多層數據_怎么解析三層List json數據

注意這個json格式不對前后的 [ ] 應該要去掉。 (我不是說你缺少的結束符)FastJSON 隨意解決的事情。0, compile com.alibaba:fastjson:1.2.71&#xff0c;去這個網站 http://www.jsonschema2pojo.org/粘貼你的json字符串1.1 Source type:JSON1.2 Annotation style:NONE1.3 所有…

《劍指Offer》37:序列化二叉樹

題目 請實現兩個函數&#xff0c;分別用來序列化和反序列化二叉樹。 分析 我們清楚可以通過前序遍歷序列和中序遍歷序列創造出一棵二叉樹。因此&#xff0c;我們可以先把一棵二叉樹序列化成一個前序遍歷序列和一個中序遍歷序列&#xff0c;然后在反序列化時通過這兩種序列還…

c linux 判斷ip合法_shell 檢測ip的合法性與檢測網絡掩碼的合法性

有時我們需要檢測IP輸入的正確性與網絡掩碼的正確性&#xff0c;用shell腳本寫的&#xff1a;#驗證ip地址的正確性check_ip_format(){echo $1 | grep "^[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}$" > /dev/nullif [ $? 1 ]; thenreturn 1elseaec…

《劍指Offer》38:字符串的排列

題目 輸入一個字符串&#xff0c;打印該字符中字符的所有排列。 例如&#xff0c;輸入字符串abc&#xff0c;則打印出由字符a、b、c所能排列出來的所有字符串有abc、acb、bac、bca、cab、cba 分析 把一個字符串看成由兩部分組成&#xff1a;第一部分是它的第一個字符&#…

含有js的英文單詞_JavaScript 常用單詞整理

JS單詞push :添加一個數組元素document &#xff1a;文檔pop &#xff1a;刪除最后一個數組元素console &#xff1a;控制臺shift &#xff1a;刪除第一個數組元素string &#xff1a;字符串Concat 組合數組undefined &#xff1a;未定義typeof &#xff1a;關鍵字join&#xf…

《劍指Offer》23:鏈表中環的入口節點

題目 若一個鏈表中包含環&#xff0c;如何找出的入口結點&#xff1f;如下圖鏈表中&#xff0c;環的入口節點的節點3。 分析 一快&#xff08;移兩節點&#xff09;一慢&#xff08;移一節點&#xff09;兩指針判斷鏈表是否存在環。算出環有幾個節點&#xff08;上一步的兩指…