亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

深入理解Java中的Lambda表達式

 更新時間:2015年07月12日 12:12:24   投稿:goldensun  
這篇文章主要介紹了深入理解Java中的Lambda表達式,Lambda在各編程語言中都是非常重要的特性,而Java中則加入得有些太晚...需要的朋友可以參考下

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

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

什么是混入 mixin?

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

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

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

class ForkingUDPServer(ForkingMixIn, UDPServer): pass
class ForkingTCPServer(ForkingMixIn, TCPServer): pass
 
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass

什么是虛擬擴展方法?


Java 8 將引入虛擬擴展方法的概念,也叫 public defender method. 讓我們姑且把這個概念簡化為 VEM。

VEM 旨在為 Java 接口提供默認的方法定義,你可以用它在已有的接口中添加新的方法定義,例如 Java 里的集合 API。這樣類似 Hibernate 這樣的第三方庫無需重復實現(xiàn)這些集合 API 的所有方法,因為已經提供了一些默認方法。

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

public interface Collection<T> extends Iterable<T> {
 
  <R> Collection<R> filter(Predicate<T> p)
    default { return Collections.<T>filter(this, p); }
 
}

Java 8 對混入的模擬

現(xiàn)在我們來通過 VEM 實現(xiàn)一個混入效果,不過事先警告的是:請不要在工作中使用!

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

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

public interface SwitchableMixin {
  boolean isActivated() default { return Switchables.isActivated(this); }
  void setActivated(boolean activated) default { Switchables.setActivated(this, activated); }
}

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

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;
  }
 
}

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

private static class Device {}
 
private static class DeviceA extends Device implements SwitchableMixin {}
 
private static class DeviceB extends Device implements SwitchableMixin {}

“完全不同的東西”

上面的實現(xiàn)跑起來似乎挺正常的,但 Oracle 的 Java 語言架構師 Brian Goetz 向我提出一個疑問說當前實現(xiàn)是無法工作的(假設線程安全和內存泄露問題已解決)
 

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í)行后會顯示什么結果呢?
疑問的解決

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

x1
x2

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

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

這兩行顯示出 makeX 方法的執(zhí)行來自兩個不同的實例,而這時當前 OpenJDK 8 生成的(這里我使用的是 OpenJDK 8 24.0-b07).

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

 

x2
x2

如果不調用 getName 方法,則顯示:
 

MyTest$$Lambda$1@5506d4ea
MyTest$$Lambda$1@5506d4ea

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

因為在編譯時,lambda 表達式并沒有經過完整的翻譯,事實上這個翻譯過程是在編譯和運行時完成的,javac 編譯器將 lambda 表達式變成 JVM 新增的指令 invokedynamic (JSR292)。這個指令包含所有必須的關于在運行時執(zhí)行 lambda 表達式的元信息。包括要調用的方法名、輸入輸出類型以及一個名為 bootstrap 的方法。bootstrap 方法用于定義接收此方法調用的實例,一旦 JVM 執(zhí)行了 invokedynamic 指令,JVM 就會在特定的 bootstrap 上調用 lambda 元工廠方法 (lambda metafactory method)。

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

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

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

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

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


其他的方法

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

所以來看看我們的 Switchable.
 

interface Switchable {  boolean isActive();
  void setActive(boolean active);
}

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

 

public interface SwitchableView extends Switchable {
  Switchable getSwitchable();
 
 
  boolean isActive() default { return getSwitchable().isActive(); }
  void setActive(boolean active) default { getSwitchable().setActive(active); }
}

接下來,我們創(chuàng)建一個完整的 Switchable 實現(xiàn):
 

public class SwitchableImpl implements Switchable {
 
 
  private boolean active;
 
 
  @Override
  public boolean isActive() {
    return active;
  }
 
 
  @Override
  public void setActive(boolean active) {
    this.active = active;
  }
}

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

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;
  }
}

結論

在這篇文章中,我們使用了兩種方法通過 Java 8 的虛擬擴展方法為類增加多個服務。第一個方法使用一個 Map 來存儲實例狀態(tài),這個方法很危險,因為不是線程安全而且存在內存泄露問題,這完全依賴于不同的 JVM 對 Java 語言的實現(xiàn)。另外一個方法是使用虛擬字段模式,通過一個抽象的 getter 來返回最終的實現(xiàn)實例。第二種方法更加獨立而且更加安全。

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

相關文章

  • Mybatis框架中Interceptor接口的使用說明

    Mybatis框架中Interceptor接口的使用說明

    這篇文章主要介紹了Mybatis框架中Interceptor接口的使用說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 基于Spring-Security自定義登陸錯誤提示信息

    基于Spring-Security自定義登陸錯誤提示信息

    這篇文章主要介紹了Spring-Security自定義登陸錯誤提示信息,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-12-12
  • java注解的類型知識點總結

    java注解的類型知識點總結

    在本篇文章里小編給大家整理了一篇關于java注解的類型知識點總結內容,有興趣的朋友們可以學習下。
    2021-03-03
  • Java時間復雜度、空間復雜度的深入詳解

    Java時間復雜度、空間復雜度的深入詳解

    對于一個算法,其時間復雜度和空間復雜度往往是相互影響的,當追求一個較好的時間復雜度時,可能會使空間復雜度的性能變差,即可能導致占用較多的存儲空間,這篇文章主要給大家介紹了關于Java時間復雜度、空間復雜度的相關資料,需要的朋友可以參考下
    2021-11-11
  • Springboot整合hibernate validator 全局異常處理步驟詳解

    Springboot整合hibernate validator 全局異常處理步驟詳解

    本文分步驟給大家介紹Springboot整合hibernate validator 全局異常處理,補呢文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • spring boot中xalan引入報錯系統(tǒng)找不到指定的文件原因分析

    spring boot中xalan引入報錯系統(tǒng)找不到指定的文件原因分析

    這篇文章主要介紹了spring boot中xalan引入報錯系統(tǒng)找不到指定的文件,主要原因是內嵌的tomcat9.0.36,本文給大家分享最新解決方法,需要的朋友可以參考下
    2023-08-08
  • Java Swing程序設計實戰(zhàn)

    Java Swing程序設計實戰(zhàn)

    今天教大家怎么用JavaSwing工具包實現(xiàn)一個程序的界面設計,文中有非常詳細的代碼示例及注釋,對正在學習Java的小伙伴們很有幫助,需要的朋友可以參考下
    2021-05-05
  • Java求兩集合中元素交集的四種方法對比分析

    Java求兩集合中元素交集的四種方法對比分析

    這篇文章主要介紹了Java求兩集合中元素交集的四種方法對比總結,四種求集合中元素交集的方法,按照在處理大量數(shù)據(jù)的效率來看,使用map集合的特性的方法效率最高,之后是使用Java流的方法,其次是使用for循環(huán)和迭代器的方法,需要的朋友可以參考下
    2023-05-05
  • SpringBoot原生組件注入實現(xiàn)兩種方式介紹

    SpringBoot原生組件注入實現(xiàn)兩種方式介紹

    SpringBoot是Spring全家桶的成員之一,基于約定優(yōu)于配置的思想(即有約定默認值,在不配置的情況下會使用默認值,在配置文件下配置的話會使用配置的值)。SpringBoot是一種整合Spring技術棧的方式(或者說是框架),同時也是簡化Spring的一種快速開發(fā)的腳手架
    2022-10-10
  • Java中Prime算法的原理與實現(xiàn)詳解

    Java中Prime算法的原理與實現(xiàn)詳解

    Prime算法是一種窮舉查找算法來從一個連通圖中構造一棵最小生成樹。本文主要為大家介紹了Java中Prime算法的原理與實現(xiàn),感興趣的可以學習一下
    2022-07-07

最新評論