首頁  >  問答  >  主體

java - 匿名內部類別中的this,如何在lambda中獲得?

nsv_productdetail.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                int bottomMargin = ((ViewGroup.MarginLayoutParams) nsv_productdetail.getLayoutParams()).bottomMargin;
                nsv_productdetail.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });

上方程式碼要是轉換成lambda時,this就會指向外部類,這時候就不能移除監聽了。
所以我想請問下,匿名內部類別(可以轉換為lambda的匿名內部類別),如何在lambda中獲得其本身?

阿神阿神2675 天前1063

全部回覆(2)我來回復

  • PHP中文网

    PHP中文网2017-06-23 09:15:26

    取代某些匿名內部類別
    本節將介紹如何使用Lambda表達式簡化匿名內部類別的書寫,但Lambda表達式並不能取代所有的匿名內部類別,只能用來取代函數介面(Functional Interface)的簡寫。先別在乎細節,看幾個例子再說。

    範例1:無參函數的簡寫
    如果需要新建一個線程,一種常見的寫法是這樣:

    // JDK7 匿名內部類別寫法
    new Thread(new Runnable(){// 介面名稱

    @Override
    public void run(){// 方法名
        System.out.println("Thread run()");
    }

    }).start();
    上述程式碼給Tread類別傳遞了一個匿名的Runnable對象,重載Runnable介面的run()方法來實現對應邏輯。這是JDK7以及之前常見的寫法。匿名內部類別省去了為類取名字的煩惱,但還是不夠簡化,在Java 8中可以簡化為如下形式:

    // JDK8 Lambda表達式寫法
    new Thread(

        () -> System.out.println("Thread run()")// 省略接口名和方法名

    ).start();
    上述程式碼跟匿名內部類別的作用是一樣的,但比匿名內部類別更進一步。這裡連接口名和函數名都一同省掉了,寫起來更神清氣爽。如果函數體有多行,可以用大括號括起來,就像這樣:

    // JDK8 Lambda表達式碼塊寫法
    new Thread(

        () -> {
            System.out.print("Hello");
            System.out.println(" Hoolee");
        }

    ).start();
    範例2:帶參函數的簡寫
    如果要給一個字串列表透過自訂比較器,按照字串長度進行排序,Java 7的書寫形式如下:

    // JDK7 匿名內部類別寫法
    List list = Arrays.asList("I", "love", "you", "too");
    Collections.sort(list, new Comparator() {// 介面名

    @Override
    public int compare(String s1, String s2){// 方法名
        if(s1 == null)
            return -1;
        if(s2 == null)
            return 1;
        return s1.length()-s2.length();
    }

    });
    上述程式碼透過內部類別重載了Comparator介面的compare()方法,實現比較邏輯。採用Lambda表達式可簡寫如下:

    // JDK8 Lambda表達式寫法
    List list = Arrays.asList("I", "love", "you", "too");
    Collections.sort(list, (s1, s2) -> {// 省略參數表的型別

    if(s1 == null)
        return -1;
    if(s2 == null)
        return 1;
    return s1.length()-s2.length();

    });
    上述程式碼跟匿名內部類別的作用是一樣的。除了省略了介面名和方法名,程式碼中把參數表的型別也省略了。這得歸功於javac的類型推斷機制,編譯器能夠根據上下文資訊推斷出參數的類型,當然也有推斷失敗的時候,這時就需要手動指明參數類型了。請注意,Java是強型別語言,每個變數和物件都必須有明確的類型。

    簡寫的依據
    也許你已經想到了,能夠使用Lambda的依據是必須有相應的函數接口(函數接口,是指內部只有一個抽象方法的接口)。這一點跟Java是強型別語言吻合,也就是說你並不能在程式碼的任何地方任性的寫Lambda表達式。實際上Lambda的類型就是對應函數介面的類型。 Lambda表達式另一個依據是類型推斷機制,在上下文資訊足夠的情況下,編譯器可以推斷參數表的類型,而不需要明確指名。 Lambda表達更多合法的書寫形式如下:

    // Lambda表達式的書寫形式
    Runnable run = () -> System.out.println("Hello World");// 1
    ActionListener listener = event -> System.out.println("button clicked") ;// 2
    Runnable multiLine = () -> {// 3 程式碼區塊

    System.out.print("Hello");
    System.out.println(" Hoolee");

    };
    BinaryOperator add = (Long x, Long y) -> x + y;// 4
    BinaryOperator addImplicit = (x, y) -> x + y;// 5 類型推斷
    上述代碼中,1展示了無參函數的簡寫;2處展示了有參函數的簡寫,以及類型推斷機制;3是代碼塊的寫法;4和5再次展示了類型推斷機制。

    自訂函數介面
    自訂函數介面很容易,只需要寫一個只有一個抽象方法的介面即可。

    // 自訂函數介面
    @FunctionalInterface
    public interface ConsumerInterface{

    void accept(T t);

    }
    上面程式碼中的@FunctionalInterface是可選的,但加上該標註編譯器會幫你檢查介面是否符合函數介面規格。就像加入@Override標註會檢查是否重載了函數一樣。

    有了上述介面定義,就可以寫出類似如下的程式碼:

    ConsumerInterface consumer = str -> System.out.println(str);

    進一步的,還可以這樣使用:

    class MyStream{

    private List<T> list;
    ...
    public void myForEach(ConsumerInterface<T> consumer){// 1
        for(T t : list){
            consumer.accept(t);
        }
    }

    }
    MyStream stream = new MyStream();
    stream.myForEach(str -> System.out.println(str));// 使用自訂函數介面書寫Lambda表達式

    回覆
    0
  • 女神的闺蜜爱上我

    女神的闺蜜爱上我2017-06-23 09:15:26

    做不到的,Lambda本身不是物件。

    回覆
    0
  • 取消回覆