一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專(zhuān)注于服務(wù)器技術(shù)及軟件下載分享
分類(lèi)導(dǎo)航

PHP教程|ASP.NET教程|JAVA教程|ASP教程|

服務(wù)器之家 - 編程語(yǔ)言 - JAVA教程 - 深入理解Java中的Lambda表達(dá)式

深入理解Java中的Lambda表達(dá)式

2019-12-26 13:27goldensun JAVA教程

這篇文章主要介紹了深入理解Java中的Lambda表達(dá)式,Lambda在各編程語(yǔ)言中都是非常重要的特性,而Java中則加入得有些太晚...需要的朋友可以參考下

 Java 8 開(kāi)始出現(xiàn),帶來(lái)一個(gè)全新特性:使用 Lambda 表達(dá)式 (JSR-335) 進(jìn)行函數(shù)式編程。今天我們要討論的是 Lambda 的其中一部分:虛擬擴(kuò)展方法,也叫做公共辯護(hù)(defender)方法。該特性可以讓你在接口定義中提供方法的默認(rèn)實(shí)現(xiàn)。例如你可以為已有的接口(如 List 和 Map)聲明一個(gè)方法定義,這樣其他開(kāi)發(fā)者就無(wú)需重新實(shí)現(xiàn)這些方法,有點(diǎn)像抽象類(lèi),但實(shí)際卻是接口。當(dāng)然,Java 8 理論上還是兼容已有的庫(kù)。

虛擬擴(kuò)展方法為 Java 帶來(lái)了多重繼承的特性,盡管該團(tuán)隊(duì)聲稱(chēng)與多重繼承不同,虛擬擴(kuò)展方法被限制用于行為繼承。或許通過(guò)這個(gè)特性你可以看到了多重繼承的影子。但你還是可以模擬實(shí)例狀態(tài)的繼承。我將在接下來(lái)的文章詳細(xì)描述 Java 8 中通過(guò) mixin 混入實(shí)現(xiàn)狀態(tài)的繼承。

什么是混入 mixin?

混入是一種組合的抽象類(lèi),主要用于多繼承上下文中為一個(gè)類(lèi)添加多個(gè)服務(wù),多重繼承將多個(gè) mixin 組合成你的類(lèi)。例如,如果你有一個(gè)類(lèi)表示“馬”,你可以實(shí)例化這個(gè)類(lèi)來(lái)創(chuàng)建一個(gè)“馬”的實(shí)例,然后通過(guò)繼承像“車(chē)庫(kù)”和“花園”來(lái)擴(kuò)展它,使用 Scala 的寫(xiě)法就是:
 
val myHouse = new House with Garage with Garden

從 mixin 繼承并不是一個(gè)特定的規(guī)范,這只是用來(lái)將各種功能添加到已有類(lèi)的方法。在 OOP 中,有了 mixin,你就有通過(guò)它來(lái)提升類(lèi)的可讀性。

例如在 Python 的  socketserver 模塊中就有使用 mixin 的方法,在這里,mixin 幫助 4 個(gè)基于不同 Socket 的 服務(wù),包括支持多進(jìn)程的 UDP 和 TCP 服務(wù)以及支持多線(xiàn)程的 UDP 和 TCP 服務(wù)。
 

?
1
2
3
4
5
class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass
 
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

什么是虛擬擴(kuò)展方法?


Java 8 將引入虛擬擴(kuò)展方法的概念,也叫 public defender method. 讓我們姑且把這個(gè)概念簡(jiǎn)化為 VEM。

VEM 旨在為 Java 接口提供默認(rèn)的方法定義,你可以用它在已有的接口中添加新的方法定義,例如 Java 里的集合 API。這樣類(lèi)似 Hibernate 這樣的第三方庫(kù)無(wú)需重復(fù)實(shí)現(xiàn)這些集合 API 的所有方法,因?yàn)橐呀?jīng)提供了一些默認(rèn)方法。

下面是如何在接口中定義方法的示例:
 

?
1
2
3
4
5
6
public interface Collection<T> extends Iterable<T> {
 
  <R> Collection<R> filter(Predicate<T> p)
    default { return Collections.<T>filter(this, p); }
 
}

Java 8 對(duì)混入的模擬

現(xiàn)在我們來(lái)通過(guò) VEM 實(shí)現(xiàn)一個(gè)混入效果,不過(guò)事先警告的是:請(qǐng)不要在工作中使用!

下面的實(shí)現(xiàn)不是線(xiàn)程安全的,而且還可能存在內(nèi)存泄露問(wèn)題,這取決于你在類(lèi)中定義的 hashCode 和 equals 方法,這也是另外一個(gè)缺點(diǎn),我將在后面討論這個(gè)問(wèn)題。

首先我們定義一個(gè)接口(模擬狀態(tài)Bean)并提供方法的默認(rèn)定義:
 

?
1
2
3
4
public interface SwitchableMixin {
  boolean isActivated() default { return Switchables.isActivated(this); }
  void setActivated(boolean activated) default { Switchables.setActivated(this, activated); }
}

然后我們定義一個(gè)工具類(lèi),包含一個(gè) Map 實(shí)例來(lái)保存實(shí)例和狀態(tài)的關(guān)聯(lián),狀態(tài)通過(guò)工具類(lèi)中的私有的嵌套類(lèi)代表:
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public final class Switchables {
 
  private static final Map<SwitchableMixin, SwitchableDeviceState> SWITCH_STATES = new HashMap<>();
 
  public static boolean isActivated(SwitchableMixin device) {
    SwitchableDeviceState state = SWITCH_STATES.get(device);
    return state != null && state.activated;
  }
 
  public static void setActivated(SwitchableMixin device, boolean activated) {
    SwitchableDeviceState state = SWITCH_STATES.get(device);
    if (state == null) {
      state = new SwitchableDeviceState();
      SWITCH_STATES.put(device, state);
    }
    state.activated = activated;
  }
 
  private static class SwitchableDeviceState {
    private boolean activated;
  }
 
}

這里是一個(gè)使用用例,突出了狀態(tài)的繼承:
 

?
1
2
3
4
5
private static class Device {}
 
private static class DeviceA extends Device implements SwitchableMixin {}
 
private static class DeviceB extends Device implements SwitchableMixin {}

“完全不同的東西”

上面的實(shí)現(xiàn)跑起來(lái)似乎挺正常的,但 Oracle 的 Java 語(yǔ)言架構(gòu)師 Brian Goetz 向我提出一個(gè)疑問(wèn)說(shuō)當(dāng)前實(shí)現(xiàn)是無(wú)法工作的(假設(shè)線(xiàn)程安全和內(nèi)存泄露問(wèn)題已解決)
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface FakeBrokenMixin {
  static Map<FakeBrokenMixin, String> backingMap
    = Collections.synchronizedMap(new WeakHashMap<FakeBrokenMixin, String>());
 
  String getName() default { return backingMap.get(this); }
  void setName(String name) default { backingMap.put(this, name); }
}
 
interface X extends Runnable, FakeBrokenMixin {}
 
X makeX() { return () -> { System.out.println("X"); }; }
 
  X x1 = makeX();
  X x2 = makeX();
  x1.setName("x1");
  x2.setName("x2");
 
  System.out.println(x1.getName());
  System.out.println(x2.getName());


你猜這段代碼執(zhí)行后會(huì)顯示什么結(jié)果呢?
疑問(wèn)的解決

第一眼看去,這個(gè)實(shí)現(xiàn)的代碼沒(méi)有問(wèn)題。X 是一個(gè)只包含一個(gè)方法的接口,因?yàn)?getName 和 setName 已經(jīng)有了默認(rèn)的定義,但 Runable 接口的 run 方法沒(méi)有定義,因此我們可通過(guò) lambda 表達(dá)式來(lái)生成 X 的實(shí)例,然后提供 run 方法的實(shí)現(xiàn),就像 makeX 那樣。因此,你希望這個(gè)程序執(zhí)行后顯示的結(jié)果是:
 

?
1
2
x1
x2

如果你刪掉 getName 方法的調(diào)用,那么執(zhí)行結(jié)果變成:
 

?
1
2
MyTest$1@30ae8764
MyTest$1@123acf34

這兩行顯示出 makeX 方法的執(zhí)行來(lái)自?xún)蓚€(gè)不同的實(shí)例,而這時(shí)當(dāng)前 OpenJDK 8 生成的(這里我使用的是 OpenJDK 8 24.0-b07).

不管怎樣,當(dāng)前的 OpenJDK 8 并不能反映最終的 Java 8 的行為,為了解決這個(gè)問(wèn)題,你需要使用特殊參數(shù) -XDlambdaToMethod 來(lái)運(yùn)行 javac 命令,在使用了這個(gè)參數(shù)后,運(yùn)行結(jié)果變成:

 

?
1
2
x2
x2

如果不調(diào)用 getName 方法,則顯示:
 

?
1
2
MyTest$$Lambda$1@5506d4ea
MyTest$$Lambda$1@5506d4ea

每個(gè)調(diào)用 makeX 方法似乎都是來(lái)自相同匿名內(nèi)部類(lèi)的一個(gè)單例實(shí)例,如果觀(guān)察包含編譯后的 java class 文件的目錄,會(huì)發(fā)現(xiàn)并沒(méi)有一個(gè)名為 MyTestClass$$Lambda$1.class 的文件。

因?yàn)樵诰幾g時(shí),lambda 表達(dá)式并沒(méi)有經(jīng)過(guò)完整的翻譯,事實(shí)上這個(gè)翻譯過(guò)程是在編譯和運(yùn)行時(shí)完成的,javac 編譯器將 lambda 表達(dá)式變成 JVM 新增的指令 invokedynamic (JSR292)。這個(gè)指令包含所有必須的關(guān)于在運(yùn)行時(shí)執(zhí)行 lambda 表達(dá)式的元信息。包括要調(diào)用的方法名、輸入輸出類(lèi)型以及一個(gè)名為 bootstrap 的方法。bootstrap 方法用于定義接收此方法調(diào)用的實(shí)例,一旦 JVM 執(zhí)行了 invokedynamic 指令,JVM 就會(huì)在特定的 bootstrap 上調(diào)用 lambda 元工廠(chǎng)方法 (lambda metafactory method)。

再回到剛才那個(gè)疑問(wèn)中,lambda 表達(dá)式轉(zhuǎn)成了一個(gè)私有的靜態(tài)方法,() -> { System.out.println("X"); } 被轉(zhuǎn)到了 MyTest:
 

?
1
2
3
private static void lambda$0() {
  System.out.println("X");
}

如果你用 javap 反編譯器并使用 -private 參數(shù)就可以看到這個(gè)方法,你也可以使用 -c 參數(shù)來(lái)查看更加完整的轉(zhuǎn)換。

當(dāng)你運(yùn)行程序時(shí),JVM 會(huì)調(diào)用 lambda metafactory method 來(lái)嘗試闡釋 invokedynamic 指令。在我們的例子中,首次調(diào)用 makeX 時(shí),lambda metafactory method 生成一個(gè) X 的實(shí)例并動(dòng)態(tài)鏈接 run 方法到 lambda$0 方法. X 的實(shí)例接下來(lái)被存儲(chǔ)在內(nèi)存中,當(dāng)?shù)诙握{(diào)用 makeX 時(shí)就直接從內(nèi)存中讀取這個(gè)實(shí)例,因此你第二次調(diào)用的實(shí)例跟第一次是一樣的。
修復(fù)了嗎?有解決辦法嗎?

目前尚無(wú)這個(gè)問(wèn)題直接的修復(fù)或者是解決辦法。盡管 Oracle 的 Java 8 計(jì)劃默認(rèn)激活-XDlambdaToMethod 參數(shù),因?yàn)檫@個(gè)參數(shù)并不是 JVM 規(guī)范的一部分,因此不同供應(yīng)商和 JVM 的實(shí)現(xiàn)是不同的。對(duì)一個(gè) lambda 表達(dá)式而言,你唯一能期望的就是在類(lèi)中實(shí)現(xiàn)你的接口方法。


其他的方法

到此為止,盡管我們對(duì) mixin 的模仿并不能兼容 Java 8,但還是可能通過(guò)多繼承和委派為已有的類(lèi)添加多個(gè)服務(wù)。這個(gè)方法就是 virtual field pattern (虛擬字段模式).

所以來(lái)看看我們的 Switchable.
 

?
1
2
3
interface Switchable {  boolean isActive();
  void setActive(boolean active);
}

我們需要一個(gè)基于 Switchable 的接口,并提供一個(gè)附加的抽象方法返回 Switchable 的實(shí)現(xiàn)。集成的方法包含默認(rèn)的定義,它們使用 getter 來(lái)轉(zhuǎn)換到 Switchable 實(shí)現(xiàn)的調(diào)用:

 

?
1
2
3
4
5
6
7
public interface SwitchableView extends Switchable {
  Switchable getSwitchable();
 
 
  boolean isActive() default { return getSwitchable().isActive(); }
  void setActive(boolean active) default { getSwitchable().setActive(active); }
}

接下來(lái),我們創(chuàng)建一個(gè)完整的 Switchable 實(shí)現(xiàn):
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SwitchableImpl implements Switchable {
 
 
  private boolean active;
 
 
  @Override
  public boolean isActive() {
    return active;
  }
 
 
  @Override
  public void setActive(boolean active) {
    this.active = active;
  }
}

這里是我們使用虛擬字段模式的例子:
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Device {}
 
 
public class DeviceA extends Device implements SwitchableView {
  private Switchable switchable = new SwitchableImpl();
 
 
  @Override
  public Switchable getSwitchable() {
    return switchable;
  }
}
 
 
public class DeviceB extends Device implements SwitchableView {
  private Switchable switchable = new SwitchableImpl();
 
 
  @Override
  public Switchable getSwitchable() {
    return switchable;
  }
}

結(jié)論

在這篇文章中,我們使用了兩種方法通過(guò) Java 8 的虛擬擴(kuò)展方法為類(lèi)增加多個(gè)服務(wù)。第一個(gè)方法使用一個(gè) Map 來(lái)存儲(chǔ)實(shí)例狀態(tài),這個(gè)方法很危險(xiǎn),因?yàn)椴皇蔷€(xiàn)程安全而且存在內(nèi)存泄露問(wèn)題,這完全依賴(lài)于不同的 JVM 對(duì) Java 語(yǔ)言的實(shí)現(xiàn)。另外一個(gè)方法是使用虛擬字段模式,通過(guò)一個(gè)抽象的 getter 來(lái)返回最終的實(shí)現(xiàn)實(shí)例。第二種方法更加獨(dú)立而且更加安全。

虛擬擴(kuò)展方法是 Java 的新特性,本文主要介紹的是多重繼承的實(shí)現(xiàn),詳細(xì)你會(huì)有更深入的研究以及應(yīng)用于其他方面,別忘了跟大家分享。

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 欧美日韩精品一区二区三区高清视频 | 国产老熟 | 99资源站 | 91亚洲精品第一综合不卡播放 | 暖暖免费高清完整版观看日本 | 小sao货ji巴cao死你视频 | 欧美香蕉视频 | 日韩一区二区不卡 | 手机在线观看国产精选免费 | 丰满大屁股美女一级毛片 | 日韩精品一区二区三区中文版 | 欧美艳星kagneyiynn高清 | 日本另类z0zx高清 | 国产网站免费在线观看 | 四虎影音先锋 | 国产精品免费看香蕉 | 国产精品一区二区三区免费 | 国产免费好大好硬视频 | 边摸边吃奶边做爽视频免费 | 欧美另类变态 | 色人阁小说 | 成人午夜爽爽爽免费视频 | 亚洲AV无码国产精品色午夜情 | 火影小南被爆羞羞网站进入 | 日韩欧美不卡视频 | 国产午夜不卡 | 国产99青草全福视在线 | 好猛好紧好硬使劲好大刺激视频 | ai换脸杨幂被c在线观看 | 久久综合亚洲色hezyo | 好大好硬好湿好紧h | 国产馆在线观看免费的 | 午夜办公室 | 精品国语国产在线对白 | 国产亚洲人成网站在线观看不卡 | 久久久久久免费高清电影 | 成人精品亚洲 | 精品国产91久久久久久久a | 日本福利视频一区 | 精品国产一区二区三区久久影院 | 国产在线精品成人一区二区三区 |