Java入門系列05 -- 接口,多態,匿名類,Lambda表達式,方法引用

接口Interface

  • 一系列方法聲明的集合;
  • 用來定義規范,標準;
  • 接口中可以定義抽象方法,常量,嵌套類型,從Java8開始可以定義:默認方法,靜態方法;
  • 上述可以定義的內容,都是隱式public的,因此可以省略public關鍵字;
  • 從Java9開始可以定義private方法,不常用;
  • 常量可以省略static,final;
  • 抽象 方法可以省略abstract關鍵字;
  • 不能自定義構造方法,不能定義(靜態)初始化塊,不能實例化;
public interface Test {
    public static final int AGE = 100;
    public int AGEE = 100;
    int AGEEE = 100;

    public abstract void test1();
    public void test3();
    void test2();

    class A {

    }
}
接口的細節
  • 接口名稱可以在任何使用類型的地方進行使用;
  • 一個類可以通過implements關鍵字實現一個或多個接口;
  • 實現接口的類必須實現接口中定義的所有抽象方法,除非它是個抽象類;
  • 如果一個類實現的多個接口中相同的抽象方法,只需要實現此方法一次;
  • extend與implements可以一起使用,但implements必須寫在extend的后面
  • 當父類,接口中的方法簽名相同時,那么這兩個方法的返回值類型也必須相同;
  • 一個接口可以通過extend關鍵字繼承一個或者多個接口,接口支持繼承
  • 當多個父接口中方法簽名相同時,那么返回值類型也必須相同;
抽象類與接口的對比
  • 抽象類,重點在于繼承,表明類是什么;
  • 接口,重點在于實現,表明類可以做什么;
  • 使用抽象類的場景:
    • 在緊密相關的類之間共享代碼時;
    • 需要除public之外的訪問權限;
    • 需要定義實例變量,非final的靜態變量;
  • 使用接口的場景:
    • 不相關的類實現相同的方法;
    • 只是定義特定數據類型的行為,不關心具體是誰實現了行為;
    • 想實現類型的多重繼承,接口能多繼承,而抽象類只能單繼承;
接口的升級問題
  • 如果接口需要升級,比如增加了新的抽象方法,會導致大幅度的代碼改動,以前實現接口的類都需要改動,若想在不改動以前實現類的前提下進行接口升級,有方案如下:
    • 默認方法;
    • 靜態方法;
接口_默認方法
  • 用default修飾默認方法;
  • 默認方法只能是實例方法;
  • 當一個類實現的接口中有默認方法時,這個類可以:
    • 啥都不干,沿用接口的默認實現;
    • 重寫默認方法,覆蓋默認方法的實現;
    • 重新聲明默認方法,將默認方法聲明為抽象方法,此類必須是抽象類;
import android.util.Log;

public interface Test {
    public abstract void test1();

    //默認方法 允許有實現
    public default void test2() {
        Log.v("Test","test2");
    }
}
import android.util.Log;

import com.example.java_test.java.interfaces.Test;

public class Dog implements Test {
    
    @Override
    public void test1() {
        Log.v("Dog","test1");
    }

    //Dog類可以不實現 也可實現
    @Override
    public void test2() {
        //實現類 可調用 接口中的默認實現
        Test.super.test2();
    }
}
import com.example.java_test.java.interfaces.Test;

//重新聲明為抽象方法 自身為抽象類
public abstract class Dog implements Test {
    @Override
    public abstract void test2();
}
  • 當一個接口繼承的父接口中有默認方法時,這個接口可以:
    • 啥都不干,沿用接口的默認實現;
    • 重寫默認方法,覆蓋默認方法的實現;
    • 重新聲明默認方法,將默認方法聲明為抽象方法;
默認方法的細節
  • 如果父類定義的非抽象方法與接口的默認方法相同時,最終將調用父類的方法,父類方法的優先級高;
import android.util.Log;

public class Animal {

    public void run() {
        Log.v("Animal","Animal" + "run");
    }
}
import android.util.Log;

public interface Runable {

    default void run() {
        Log.v("Runable","Runable" + "run");
    }
}
public class Dog extends Animal implements Runnable{

}
  • 如果父類定義的抽象方法與接口的默認方法相同時,要求子類實現此抽象方法;
    • 可通過super關鍵字調用接口的默認方法;
public abstract class Animal {
    public abstract void run();
}
import android.util.Log;

public interface Runable {

    default void run() {
        Log.v("Runable","Runable" + "run");
    }
}
public class Dog extends Animal implements Runnable{

    @Override
    public void run() {

    }
}
  • 如果(父)接口定義的默認方法與其他(父)接口定義的方法相同時,要求子類實現此默認方法;
import android.util.Log;

public interface Runable {

    default void run() {
        Log.v("Runable","Runable" + "run");
    }
}
import android.util.Log;

public interface Walkable {
    default void run() {
        Log.v("Walkable","Walkable" + "run");
    }
}
public interface Testable extends Runable,Walkable{
    @Override
    default void run() {
        Runable.super.run();
        Walkable.super.run();
    }
}
import com.example.java_test.java.interfaces.Runable;
import com.example.java_test.java.interfaces.Walkable;

public class Dog implements Runable, Walkable {

    @Override
    public void run() {
        Runable.super.run();
        Walkable.super.run();
    }
}
  • Testable與Dog都必須實現run默認方法;
  • 如果類實現的兩個接口中有相同的默認方法,那么類必須實現默認方法,如果兩個接口中只有其中一個接口實現了默認方法,那么類可以不用實現默認方法,規則是類能確定自己調用的是哪個方法;
public interface Animal {
    default String myself() {
        return "I am an animal";
    }
}
public interface Fire extends Animal{

}
public interface Fly extends Animal{
    default String myself() {
        return "I am able to fly";
    }
}
import com.example.java_test.java.interfaces.Fire;
import com.example.java_test.java.interfaces.Fly;

public class Dragon implements Fly, Fire {

}
接口_靜態方法
  • 接口中定義的靜態方法只能通過接口名調用,不能被繼承;
import android.util.Log;

public interface Eatable {

    public static void eat(String name) {
        Log.v("Eatable","eat");
    }
}
import android.util.Log;

public interface Sleepable {
    public static void eat(String name) {
        Log.v("Sleepable","eat");
    }
}
import android.util.Log;

public interface Dog extends Eatable,Sleepable{
    public static void eat(String name) {
        Log.v("Dog","eat");
    }
}
import com.example.java_test.java.interfaces.Dog;
import com.example.java_test.java.interfaces.Eatable;
import com.example.java_test.java.interfaces.Sleepable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        Dog.eat("1");
        Eatable.eat("1");
        Sleepable.eat("1");
    }
}

多態

  • 同一操作作用于不同的對象,產生不同的執行結果;
  • 父類(接口) 指針指向子類的對象;
  • 調用子類重寫的方法;
  • JVM會根據引用變量指向的具體對象來調用對應的方法,這個行為叫做:虛方法調用,類似C++中的虛函數調用;
  • 類與接口都可以實現多態;
  • 實例方法,會根據對象本身的類型,確定方法的調用;
類方法的調用細節
  • 在Java中只有實例方法存在重寫,類方法是不存在重寫的;
  • 與引用類型關聯;
成員變量的訪問細節
  • 與引用類型關聯;
instanceof
  • 可以通過instanceof判斷某個類型是否屬于某種類型;
public class Animal {
    
}
import android.util.Log;

public interface Eatable {

    public static void eat(String name) {
        Log.v("Eatable","eat");
    }
}
import com.example.java_test.java.interfaces.Eatable;

public class Dog extends Animal implements Eatable {

}
import com.example.java_test.java.cls.Animal;
import com.example.java_test.java.cls.Dog;
import com.example.java_test.java.interfaces.Eatable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Object dog = new Dog();
        Log.v("MainActivity - 1",String.valueOf(dog instanceof Dog)); //true
        Log.v("MainActivity - 2",String.valueOf(dog instanceof Animal)); //true
        Log.v("MainActivity - 3",String.valueOf(dog instanceof Eatable)); //true
        Log.v("MainActivity - 4",String.valueOf(dog instanceof String)); //false
    }
}
  • 類型的強制轉換
Object obj = new Cat();
Cat cat = ((Cat) obj);
  • 接口用來實現 面向接口編程,減少類之間的耦合性;

匿名類

  • 當接口,抽象類的實現類,在整個項目中只用過一次,可以考慮使用匿名類;
import com.example.java_test.java.interfaces.Eatable;

public class Person implements Eatable {
    @Override
    public void eat() {
        Log.v("eat","person - eat");
    }
}
public interface Eatable {
    void eat();
}
import com.example.java_test.java.cls.Person;
import com.example.java_test.java.interfaces.Eatable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Person person = new Person();
        person.eat();
    }
}
  • 使用匿名類,Person類文件可以直接刪除;
import com.example.java_test.java.interfaces.Eatable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Eatable person = new Eatable() {
            @Override
            public void eat() {

            }
        };
        person.eat();
    }
}
  • 使用匿名類,直接調用方法;
import com.example.java_test.java.interfaces.Eatable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //匿名類
         new Eatable() {
            @Override
            public void eat() {
                Log.v("eat","person - eat");
            }
        }.eat();
    }
}
  • 匿名類的注意點:

    • 匿名類不能定義除編譯時常量以外的任何static成員;
    • 匿名類只能訪問final 或者 有效final的局部變量,跟局部類類似;
    • 匿名類可以直接訪問外部類的所有成員(即使被聲明為private)
      • 匿名類只有在實例相關的代碼塊中使用,才能直接訪問外部類中的實例成員(實例變量,實例方法);
    • 匿名類不能定義構造方法,可以定義初始化塊;
  • 匿名類的使用場景:

    • 代碼傳遞;
    • 過濾器;
    • 回調;
匿名類實現代碼傳遞
package com.example.java_test.java.cls;

import android.util.Log;

public class TimeUtils {
    
    public interface Block {
        void execute();
    }
    //block 是一個實現Block接口的匿名類 實例對象
    public static void test(Block block) {
        double begin = System.currentTimeMillis();
        //匿名類 實例對象 調用接口方法
        block.execute();
        double end = System.currentTimeMillis();
        double duration = (end - begin) / 1000.0;
        Log.v("TimeUtils",String.valueOf(duration));
    }
}
import com.example.java_test.java.cls.TimeUtils;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TimeUtils.test(new TimeUtils.Block() {
            @Override
            public void execute() {
                test();
            }
        });
    }

    public static void test() {
        int num = 100000;
        String str = "asd";
        for (int i = 0; i < num;i++) {
            str += i;
        }
    }
}
  • 實現 統計一段代碼執行的耗時時間,采用匿名類的方式,將執行代碼塊 傳遞給TimeUtils,然后TimeUtils處理計算耗時時間;
  • 參數block 是一個實現Block接口的匿名類 實例對象
匿名類實現回調
public class NetworkUtils {
    public interface Block {
        void success(Object response);
        void failure();
    }

    public static void get(String url,Block callBack) {
        //根據URL 發送網絡請求

        //請求完成后
        boolean result = true;
        if (result) {
            callBack.success("success");
        } else {
            callBack.failure();
        }
    }
}
import com.example.java_test.java.cls.NetworkUtils;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        NetworkUtils.get("https.www.baidu.com", new NetworkUtils.Block() {
            @Override
            public void success(Object response) {
                Log.v("MainActivity","請求成功");
            }

            @Override
            public void failure() {
                Log.v("MainActivity","請求失敗");
            }
        });
    }
}
  • 匿名類實現回調與實現代碼傳遞的方式 時相同的;
  • 上述使用匿名類實現了 網絡請求的成功與失敗的回調;
匿名類實現條件過濾
public class FileUtils {

    public interface Filter {
        boolean accept(String fileName);
    }

    public static String[] getAllFileNames(String dir,Filter filter) {
        //1.根據dir路徑 獲取所有文件
        String[] allFileNames = {};
        //2.文件過濾
        String[] filters = {};
        for (String fileName : allFileNames) {
            if (filter.accept(fileName)) {

            }
        }
        //3.返回過濾之后的文件
        return filters;
    }
}
import com.example.java_test.java.cls.FileUtils;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        FileUtils.getAllFileNames("F://", new FileUtils.Filter() {
            @Override
            public boolean accept(String fileName) {
                return fileName.contains("類");
            }
        });
    }
}
  • 將過濾條件封裝成 匿名類,不同的過濾條件 可通過不同的匿名類來實現;
匿名類實現排序
import java.util.Arrays;
import java.util.Comparator;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Integer[] array = {23,11,59,4,423,89,56};
        Arrays.sort(array, new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        Log.v("MainActivity",Arrays.toString(array));
    }
}
  • 排序規則是通過匿名類 傳遞給數組的,其中new Comparator<Integer>() {} 是匿名類實例對象,即一個實現Comparator接口的匿名類實例對象;

Lambda Expression

  • Lambda表達式是Java8開始才有的語法;
  • 函數式接口:只包含一個抽象方法的接口;
    • 可以在接口上面加上@FunctionalInterface注解,表示它是一個函數式接口;
@FunctionalInterface
    public interface Filter {
        boolean accept(String fileName);
    }
  • 當匿名類實現的是函數式接口時,可以使用Lambda表達式進行簡化;
  • 將上面匿名類實現的統計代碼耗時,文件過濾與數組排序 利用Lambda表達式進行簡化,如下所示:
import com.example.java_test.java.cls.FileUtils;
import com.example.java_test.java.cls.TimeUtils;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //使用Lambda表達式
        TimeUtils.test(() -> {
            test();
        });

        FileUtils.getAllFileNames("F://",(String fileName) -> {
            return fileName.contains("類");
        });

        Integer[] array = {23,11,59,4,423,89,56};
        Arrays.sort(array,(Integer o1, Integer o2) -> {
            return o1 - o2;
        });
        Log.v("MainActivity",Arrays.toString(array));
    }
    public static void test() {
        int num = 100000;
        String str = "asd";
        for (int i = 0; i < num;i++) {
            str += i;
        }
    }
}
  • Lambda表達式的格式:(參數列表) -> { 函數實現 },將匿名類中實現的抽象接口方法,寫成Lambda表達式的格式;
  • Lambda表達式還可以進行簡化:
    • 參數列表可以省略參數類型;
    • 當方法實現只有一條語句時,可以省略大括號,分號,return;
    • 當方法只有一個參數時,可以省略小括號;
    • 當方法沒有參數時,不能省略小括號;
  • 再次進行簡化,如下所示:
import com.example.java_test.java.cls.FileUtils;
import com.example.java_test.java.cls.TimeUtils;
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //使用Lambda表達式
        TimeUtils.test(() -> test());

        FileUtils.getAllFileNames("F://",(fileName) -> fileName.contains("類"));

        Integer[] array = {23,11,59,4,423,89,56};
        Arrays.sort(array,(o1,o2) -> o1 - o2);
        Log.v("MainActivity",Arrays.toString(array));
    }
    public static void test() {
        int num = 100000;
        String str = "asd";
        for (int i = 0; i < num;i++) {
            str += i;
        }
    }
}
  • Lambda的使用注意點
    • Lambda只能訪問final 或者 有效final的局部變量;
    • Lambda沒有引入新的作用域;
import com.example.java_test.java.interfaces.Testable;

public class OuterClass {
    private int age = 1;

    public class InnerClass {
        private int age = 2;
        void inner() {
            //沒有引入新的作用域
            Testable t1 = v -> {
                Log.v("InnerClass",String.valueOf(v));
                Log.v("InnerClass",String.valueOf(age));
                Log.v("InnerClass",String.valueOf(this.age));
                Log.v("InnerClass",String.valueOf(InnerClass.this.age));
                Log.v("InnerClass",String.valueOf(OuterClass.this.age));
            };
            //Log.v("InnerClass",String.valueOf(v));
            //Log.v("InnerClass",String.valueOf(age));
            //Log.v("InnerClass",String.valueOf(this.age));
            //Log.v("InnerClass",String.valueOf(InnerClass.this.age));
            //Log.v("InnerClass",String.valueOf(OuterClass.this.age));
        }
    }
}
  • Testable t1 = v -> { } 大括號形同虛設;
import com.example.java_test.java.interfaces.Testable;

public class OuterClass {
    private int age = 1;

    public class InnerClass {
        private int age = 2;
        void inner() {
            //沒有引入新的作用域
            Testable t1 = v -> {
                Log.v("InnerClass",String.valueOf(v));
                Log.v("InnerClass",String.valueOf(age));
                Log.v("InnerClass",String.valueOf(this.age));
                Log.v("InnerClass",String.valueOf(InnerClass.this.age));
                Log.v("InnerClass",String.valueOf(OuterClass.this.age));
            };

            Testable t2 = new Testable() {
                @Override
                public void test(int a) {
                    Log.v("InnerClass",String.valueOf(a));
                    Log.v("InnerClass",String.valueOf(age));
                    Log.v("InnerClass",String.valueOf(this.age)); //報錯
                    Log.v("InnerClass",String.valueOf(InnerClass.this.age));
                    Log.v("InnerClass",String.valueOf(OuterClass.this.age));
                }
            };
        }
    }
}
  • t1與t2 從本質上來看,是等價的,都是創建一個實現Testable接口的匿名類實例對象,但t2的this.age會報錯,原因在于this現在是指匿名類,匿名類是沒有定義age成員的所以會報錯,t1使用了Lambda表達式,沒有引入新的作用域,不會報錯;

方法引用

  • 如果Lambda中的內容僅僅是調用某個方法,可以使用方法引用來簡化;
引用類方法
public interface Testable {
    int test(int v1,int v2);
}
import com.example.java_test.java.interfaces.Testable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Testable testable1 = ((v1, v2) -> {
            return v1 > v2 ? v1 : v2;
        });

        Testable testable2 = ((v1, v2) -> {
            return Math.max(v1,v2);
        });

        Testable testable3 = ((v1, v2) -> Math.max(v1,v2));

        //引用類方法
        Testable testable4 = Math::max;

        testable4.test(100,22);
    }
}
引用特定對象的實例方法
public interface Testable {
    void test(int v1);
}
import android.util.Log;

public class Person {

    public void setAge(int age) {
        Log.v("Person",String.valueOf(age));
    }
}
import com.example.java_test.java.cls.Person;
import com.example.java_test.java.interfaces.Testable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //引用特定對象System.out 的實例方法
        execute(v -> System.out.println(v),10);
        execute(System.out::println,10);

        //引用特定對象Person的 實例方法
        execute(v -> new Person().setAge(v),20);
        execute(new Person()::setAge,20);
    }

    public static void execute(Testable testable,int v) {
        testable.test(v);
    }
}
引用特定類型的任意對象的實例方法
import java.util.Arrays;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        String[] strings = {"abc","sff","awc","tgd"};
        Arrays.sort(strings,(s1,s2) -> s1.compareToIgnoreCase(s2));
        Log.v("MainActivity",String.valueOf(Arrays.toString(strings)));

        //引用特定類型的任意對象的實例方法
        //直接使用類名 引用方法
        Arrays.sort(strings,String::compareToIgnoreCase);
    }
}
引用構造方法
public interface Testable {
    Object test(int v1);
}
public class Person {

    public Person(int age) {
        System.out.println(age);
    }
}
import com.example.java_test.java.cls.Person;
import com.example.java_test.java.interfaces.Testable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Testable testable = v -> new Person(v);
        //引用構造方法
        Testable testable1 = Person::new;
    }
}
引用數組的構造方法
public interface Testable {
    Object test(int v1);
}
import com.example.java_test.java.interfaces.Testable;

public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Testable testable = v -> new int[v];
        //引用數組的構造方法
        Testable testable1 = int[]::new;
    }
}
引用當前類中定義的實例方法
public interface Testable {
    void test(int v1);
}
import com.example.java_test.java.interfaces.Testable;

public class Person {

    public void setAge(int age) {
        System.out.println(age);
    }

    public void show() {
        Testable testable = v -> setAge(v);
        //引用當前類中定義的實例方法
        Testable testable1 = this::setAge;
    }
}
引用父類中定義的實例方法
public interface Testable {
    void test(int v1);
}
public class Person {

    public void setAge(int age) {
        System.out.println("Person" + age);
    }
}
import com.example.java_test.java.interfaces.Testable;

public class Student extends Person{
    @Override
    public void setAge(int age) {
        System.out.println("Student" + age);
    }

    public void show() {
        Testable testable = v -> setAge(v);
        //引用當前類中定義的實例方法
        Testable testable1 = super::setAge;
    }
}
  • 總結如下:
image.png
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容