Java 8 新語(yǔ)法

接口默認(rèn)方法

可以有多個(gè)默認(rèn)方法

interface Formula {
    double calculate(int a);

    default double sqrt(int a) {
        return Math.sqrt(a);
    }
}
Formula formula = new Formula() {
    @Override
    public double calculate(int a) {
        return sqrt(a * 100);
    }
};

formula.calculate(100);     // 100.0
formula.sqrt(16);           // 4.0

Lambda 表達(dá)式(閉包)

將函數(shù)當(dāng)成參數(shù)傳遞某個(gè)方法,或者把代碼本身當(dāng)成數(shù)據(jù)處理

Lambda編程思想
聲明式編程:告訴機(jī)器你想要什么检柬,讓機(jī)器去想如何去做
什么叫函數(shù)式:一些數(shù)據(jù)給一個(gè)函數(shù)疚膊,得到另外的一些值
行為參數(shù)化:用戶(hù)需求會(huì)變蛮原;將一個(gè)代碼塊當(dāng)成一個(gè)參數(shù)傳遞給一個(gè)方法揍异,稍后去執(zhí)行這段代碼,這個(gè)方法的行為就基于代碼塊被參數(shù)化了
一個(gè)方法接收不同的行為作為參數(shù)萄传,根據(jù)內(nèi)部不同的算法饥悴,產(chǎn)生不同的結(jié)果

Lambda基本語(yǔ)法
(paramters)-> expression
或者
(paramters)-> { statements; }

在以前的JAVA版本中,list 的排序是這樣的

List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");

Collections.sort(names, new Comparator<String>() {
    @Override
    public int compare(String a, String b) {
        return b.compareTo(a);
    }
});

而JAVA 8引入了一個(gè)新概念叫做 λ 表達(dá)式盲再,上面的代碼段等同于下面:

Collections.sort(names, (String a, String b) -> {
    return b.compareTo(a);
});

甚至更短西设!

Collections.sort(names, (String a, String b) -> b.compareTo(a));

還可以再短!

names.sort((a, b) -> b.compareTo(a));

函數(shù)接口

即定義了一個(gè)抽象方法的接口
為了讓現(xiàn)有功能與 λ 表達(dá)式 有一個(gè)好的兼容
函數(shù)表達(dá)式必須包含一個(gè)抽象方法聲明答朋,這個(gè)類(lèi)型的 λ 表達(dá)式 都會(huì)匹配這個(gè)抽象方法贷揽。
為了確保接口滿(mǎn)足要求,應(yīng)該加 @FunctionalInterface 注釋?zhuān)绻÷宰⑨專(zhuān)a也是生效的

@FunctionalInterface
interface Converter<F, T> {
    T convert(F from);
}
Converter<String, Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);    // 123

方法和構(gòu)造器引用

利用靜態(tài)方法引用可以進(jìn)一步簡(jiǎn)化上述代碼

Converter<String, Integer> converter = Integer::valueOf;
Integer converted = converter.convert("123");
System.out.println(converted);   // 123

Java 8 允許用 :: 引用方法或者構(gòu)造器梦碗,上面的代碼展示了怎么引用一個(gè)靜態(tài)方法禽绪,但也可以通過(guò)以下代碼引用對(duì)象方法:

class Something {
    String startsWith(String s) {
        return String.valueOf(s.charAt(0));
    }
}
Something something = new Something();
Converter<String, String> converter = something::startsWith;
String converted = converter.convert("Java");
System.out.println(converted);    // "J"

接下來(lái)看看 :: 是怎么在構(gòu)造方法上起作用的,首先是一個(gè)擁有不同構(gòu)造方法的 Person 類(lèi)

class Person {
    String firstName;
    String lastName;

    Person() {}

    Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }
}

接下來(lái)我們使用工廠接口洪规,用于創(chuàng)建 Person 對(duì)象

interface PersonFactory<P extends Person> {
    P create(String firstName, String lastName);
}

不同于手動(dòng)實(shí)現(xiàn)這個(gè)接口印屁,利用構(gòu)造器引用可以這樣做

PersonFactory<Person> personFactory = Person::new;
Person person = personFactory.create("Peter", "Parker");

上述代碼通過(guò) Person::new 創(chuàng)造了Person構(gòu)造器引用

λ 范圍

從 λ 表達(dá)式 中訪問(wèn)外部變量與匿名函數(shù)相似

訪問(wèn)局部變量
final int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);

stringConverter.convert(2);     // 3

不同于匿名函數(shù)的是,即使 num 不聲明為 final斩例,代碼也是可以運(yùn)行的
但是如果要編譯成功雄人,num必須是隱式 final,下面的代碼塊編譯不了

int num = 1;
Converter<Integer, String> stringConverter =
        (from) -> String.valueOf(from + num);
num = 3;

在 λ 表達(dá)式中 num 為只讀

訪問(wèn)字段和靜態(tài)變量

可以在 λ 表達(dá)式中讀寫(xiě)實(shí)例字段和靜態(tài)變量

class Lambda4 {
    static int outerStaticNum;
    int outerNum;

    void testScopes() {
        Converter<Integer, String> stringConverter1 = (from) -> {
            outerNum = 23;
            return String.valueOf(from);
        };

        Converter<Integer, String> stringConverter2 = (from) -> {
            outerStaticNum = 72;
            return String.valueOf(from);
        };
    }
}
接口默認(rèn)方法

λ 表達(dá)式不能訪問(wèn)接口默認(rèn)方法

內(nèi)置的函數(shù)式接口

Predicate

布爾表達(dá)式 predicate 是一個(gè)接受一個(gè)參數(shù)念赶,返回布爾值的函數(shù)(and,or,negate)础钠,可以理解成斷言

Predicate<String> predicate = (s) -> s.length() > 0;

predicate.test("foo");              // true
predicate.negate().test("foo");     // false

Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;

Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
Function

Function接收一個(gè)參數(shù),并產(chǎn)生一個(gè)結(jié)果(compose,andThen)叉谜,可以理解成轉(zhuǎn)換類(lèi)型

Function<String, Integer> toInteger = Integer::valueOf;
Function<String, String> backToString = toInteger.andThen(String::valueOf);

backToString.apply("123");     // "123"
Supplier

創(chuàng)建對(duì)象旗吁,提供數(shù)據(jù)

Supplier<Person> personSupplier = Person::new;
personSupplier.get();   // new Person
Consumer

消費(fèi)一個(gè)對(duì)象,即在單個(gè)對(duì)象上操作停局,只有一個(gè)accept方法

Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
Comparators

比較兩個(gè)參數(shù)

Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);

Person p1 = new Person("John", "Doe");
Person p2 = new Person("Alice", "Wonderland");

comparator.compare(p1, p2);             // > 0
comparator.reversed().compare(p1, p2);  // < 0

Optionals

Optional 是一個(gè)容器很钓,其值可以是空或非空
目的是解決空指針的問(wèn)題

Optional<String> optional = Optional.of("bam");

optional.isPresent();           // true
optional.get();                 // "bam"
optional.orElse("fallback");    // "bam"

optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"

Streams

一個(gè) java.util.Stream 代表著可以執(zhí)行的一個(gè)或多個(gè)操作的元素集合香府,流操作可以是中間操作也可以是終操作。終端操作返回一個(gè)確定的類(lèi)型码倦,中間操作返回流本身企孩,所以你可以在一行鏈接多個(gè)方法。
流可以在 java.util.Collectionlistset上創(chuàng)建( Map 不行)叹洲。
流操作可以是順序的也可以是并列的柠硕。
順序流工作:

List<String> stringCollection = new ArrayList<>();
stringCollection.add("ddd2");
stringCollection.add("aaa2");
stringCollection.add("bbb1");
stringCollection.add("aaa1");
stringCollection.add("bbb3");
stringCollection.add("ccc");
stringCollection.add("bbb2");
stringCollection.add("ddd1");

Java 8 中 Collections 被擴(kuò)展了工禾,可以調(diào)用 Collection.stream() 或者 Collection.parallelStream() 簡(jiǎn)單的創(chuàng)建流

Filter

Filter 接受一個(gè)參數(shù)來(lái)過(guò)濾流的所有元素
foreach 是終操作运提,所以不能再調(diào)用其它流操作

stringCollection
    .stream()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// "aaa2", "aaa1"
Sorted

Sorted 是中間操作,默認(rèn)字典排序闻葵,可以添加自定義 comparator

stringCollection
    .stream()
    .sorted()
    .filter((s) -> s.startsWith("a"))
    .forEach(System.out::println);

// "aaa1", "aaa2"

注意 sorted 不會(huì)改變?cè)辛鞯臄?shù)據(jù)

System.out.println(stringCollection);
// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1
Map

中間操作 Map 通過(guò)給定的函數(shù)將流數(shù)據(jù)轉(zhuǎn)換成另一個(gè)對(duì)象

stringCollection
    .stream()
    .map(String::toUpperCase)
    .sorted((a, b) -> b.compareTo(a))
    .forEach(System.out::println);

// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
Match

終操作并返回一個(gè)boolean值

boolean anyStartsWithA =
    stringCollection
        .stream()
        .anyMatch((s) -> s.startsWith("a"));

System.out.println(anyStartsWithA);      // true

boolean allStartsWithA =
    stringCollection
        .stream()
        .allMatch((s) -> s.startsWith("a"));

System.out.println(allStartsWithA);      // false

boolean noneStartsWithZ =
    stringCollection
        .stream()
        .noneMatch((s) -> s.startsWith("z"));

System.out.println(noneStartsWithZ);      // true
Count

終操作并以 long 類(lèi)型返回流的數(shù)據(jù)個(gè)數(shù)

long startsWithB =
    stringCollection
        .stream()
        .filter((s) -> s.startsWith("b"))
        .count();

System.out.println(startsWithB);    // 3
Reduce

終操作民泵,并以給定的函數(shù)縮減數(shù)據(jù)

Optional<String> reduced =
    stringCollection
        .stream()
        .sorted()
        .reduce((s1, s2) -> s1 + "#" + s2);

reduced.ifPresent(System.out::println);
// "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"

并行流

Maps

Maps可以在 key,或者 value 槽畔,或者 entry 創(chuàng)建特殊的流栈妆,分別是
map.keySet().stream()
map.values().stream()
map.entrySet().stream()

Map<Integer, String> map = new HashMap<>();

for (int i = 0; i < 10; i++) {
    map.putIfAbsent(i, "val" + i);
}

map.forEach((id, val) -> System.out.println(val));

putIfAbsent 可以省略 if null 檢查

map.computeIfPresent(3, (num, val) -> val + num);
map.get(3);             // val33

map.computeIfPresent(9, (num, val) -> null);
map.containsKey(9);     // false

map.computeIfAbsent(23, num -> "val" + num);
map.containsKey(23);    // true

map.computeIfAbsent(3, num -> "bam");
map.get(3);             // val33

通過(guò)給定的key、給定的值刪除數(shù)據(jù)

map.remove(3, "val3");
map.get(3);             // val33

map.remove(3, "val33");
map.get(3);             // null

另一個(gè)有用的方法

map.getOrDefault(42, "not found");  // not found

連接兩個(gè)Map數(shù)據(jù)

map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9

map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
map.get(9);             // val9concat

如果不存在key-value的條目厢钧,則創(chuàng)建鳞尔,或者改變?cè)?/p>

Data API

Java 8 在 java.time 下有一個(gè)新的時(shí)間日期分支,

Clock

Clock 提供了現(xiàn)在的時(shí)間戳早直,可代替 System.currentTimeMillis()寥假,時(shí)間的順勢(shì)點(diǎn)由 Instant 類(lèi)表示

Clock clock = Clock.systemDefaultZone();
long millis = clock.millis();

Instant instant = clock.instant();
Date legacyDate = Date.from(instant);   // legacy java.util.Date
Timezones

Timezones由 ZoneId 表示,可以通過(guò)靜態(tài)方法被訪問(wèn)霞扬,可以定義偏移量

System.out.println(ZoneId.getAvailableZoneIds());
// prints all available timezone ids

ZoneId zone1 = ZoneId.of("Europe/Berlin");
ZoneId zone2 = ZoneId.of("Brazil/East");
System.out.println(zone1.getRules());
System.out.println(zone2.getRules());

// ZoneRules[currentStandardOffset=+01:00]
// ZoneRules[currentStandardOffset=-03:00]
LocalTime

LocalTime 表示一個(gè)不帶時(shí)區(qū)的時(shí)間糕韧,下面代碼為上面兩個(gè)時(shí)區(qū)創(chuàng)建兩個(gè)本地時(shí)間

LocalTime now1 = LocalTime.now(zone1);
LocalTime now2 = LocalTime.now(zone2);

System.out.println(now1.isBefore(now2));  // false

long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);

System.out.println(hoursBetween);       // -3
System.out.println(minutesBetween);     // -239

LocalTime提供了很多工廠方法來(lái)創(chuàng)建實(shí)例,包括時(shí)間字符串的解析

LocalTime late = LocalTime.of(23, 59, 59);
System.out.println(late);       // 23:59:59

DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedTime(FormatStyle.SHORT)
        .withLocale(Locale.GERMAN);

LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);
System.out.println(leetTime);   // 13:37
LocalDate

本地時(shí)間類(lèi)似于2014-03-11喻圃,是不可變的

LocalDate today = LocalDate.now();
LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
LocalDate yesterday = tomorrow.minusDays(2);

LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);
DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
System.out.println(dayOfWeek);    // FRIDAY

字符串解析

DateTimeFormatter germanFormatter =
    DateTimeFormatter
        .ofLocalizedDate(FormatStyle.MEDIUM)
        .withLocale(Locale.GERMAN);

LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);
System.out.println(xmas);   // 2014-12-24
LocalDateTime

將date 和 time 組成一個(gè)實(shí)例

LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);

DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
System.out.println(dayOfWeek);      // WEDNESDAY

Month month = sylvester.getMonth();
System.out.println(month);          // DECEMBER

long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
System.out.println(minuteOfDay);    // 1439
Instant instant = sylvester
        .atZone(ZoneId.systemDefault())
        .toInstant();

Date legacyDate = Date.from(instant);
System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014

格式化萤彩,DateTimeFormatter 是線(xiàn)程安全的

DateTimeFormatter formatter =
    DateTimeFormatter
        .ofPattern("MMM dd, yyyy - HH:mm");

LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);
String string = formatter.format(parsed);
System.out.println(string);     // Nov 03, 2014 - 07:13

注解

@interface Hints {
    Hint[] value();
}

@Repeatable(Hints.class)
@interface Hint {
    String value();
}

@Repeatable 允許使用同一類(lèi)型的多個(gè)注解
傳統(tǒng)方法:使用容器注解

@Hints({@Hint("hint1"), @Hint("hint2")})
class Person {}

新方法:重復(fù)使用注解

@Hint("hint1")
@Hint("hint2")
class Person {}

Java隱式地設(shè)置了注解,這對(duì)于通過(guò)反射讀取注釋信息非常重要

Hint hint = Person.class.getAnnotation(Hint.class);
System.out.println(hint);                   // null

Hints hints1 = Person.class.getAnnotation(Hints.class);
System.out.println(hints1.value().length);  // 2

Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
System.out.println(hints2.length);          // 2

盡管在 Person 類(lèi)中沒(méi)有聲明注解 @Hints 斧拍,但仍然可以通過(guò) getAnnotation(Hints.class) 讀取
更方便的方法是 getAnnotationsByType 雀扶,它允許直接訪問(wèn)所有 @Hints 注釋

Java 8 擴(kuò)展了兩個(gè)新注釋

@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
@interface MyAnnotation {}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肆汹,隨后出現(xiàn)的幾起案子怕吴,更是在濱河造成了極大的恐慌,老刑警劉巖县踢,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件转绷,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡硼啤,警方通過(guò)查閱死者的電腦和手機(jī)议经,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人煞肾,你說(shuō)我怎么就攤上這事咧织。” “怎么了籍救?”我有些...
    開(kāi)封第一講書(shū)人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵习绢,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我蝙昙,道長(zhǎng)闪萄,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任奇颠,我火速辦了婚禮败去,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烈拒。我一直安慰自己圆裕,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布荆几。 她就那樣靜靜地躺著吓妆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吨铸。 梳的紋絲不亂的頭發(fā)上行拢,一...
    開(kāi)封第一講書(shū)人閱讀 51,554評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音焊傅,去河邊找鬼剂陡。 笑死,一個(gè)胖子當(dāng)著我的面吹牛狐胎,可吹牛的內(nèi)容都是我干的鸭栖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼握巢,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼晕鹊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起暴浦,我...
    開(kāi)封第一講書(shū)人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤溅话,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后歌焦,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體飞几,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年独撇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了屑墨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片躁锁。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卵史,靈堂內(nèi)的尸體忽然破棺而出战转,到底是詐尸還是另有隱情,我是刑警寧澤以躯,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布槐秧,位于F島的核電站,受9級(jí)特大地震影響忧设,放射性物質(zhì)發(fā)生泄漏刁标。R本人自食惡果不足惜童擎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望站叼。 院中可真熱鬧始苇,春花似錦、人聲如沸退腥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乘客。三九已至,卻和暖如春淀歇,著一層夾襖步出監(jiān)牢的瞬間易核,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工浪默, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牡直,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓纳决,卻偏偏與公主長(zhǎng)得像碰逸,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子阔加,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容