JDK8漫談——代碼更優雅

簡介

lambda表達式,又稱閉包(Closure)或稱匿名方法(anonymous method)。將Lambda表達式引入JAVA中的動機源於一個叫「行爲參數」的模式。這種模式可以解決需求變化帶來的問題,使代碼變得更加靈活。在JAVA8以前,參數模式十分囉嗦。Lambda表達式經過精簡的方式使用行爲模式克服了這個缺點php

解決什麼問題

  • 傳遞行爲。它容許咱們將函數當成參數傳遞給某個方法,或者把代碼自己看成數據處理,變成了一等公民。解決重複的代碼片斷和代碼包裹問題。
  • 內置抽象行爲。把常見的行爲定義成接口,能夠直接使用,減小重複思考和代碼。都在java.util.function包裏
  • 更少的代碼。經過類型推斷,方法引用,可讓代碼更優雅。

背後思想

函數式編程html

內容說明

1. 做用域

  • this
    在內部類中,this指向當前內部類對象本身,而在lambda表達式中,this指向的是表達式外部的類對象。
public class ScopeTest {
    @Test
    public void test_scope(){

        Runnable runnable = () -> {
            this.print();
        };
        runnable.run();

    }
    private void print(){
        System.out.println("I can print");
    }
}
複製代碼
  • final
    labmda表達式使用外部的變量時,不可修改,默認定義成final java

  • 函數式接口
    只有一個抽象方法的接口咱們就稱之爲功能性接口,又簡稱 SAM 類型,即 Simple Abstract Method。會寫上註釋 @FunctionalInterface編程

//用這個註解來表示功能性接口
public interface Consumer<T> {
    /** * Performs this operation on the given argument. * * @param t the input argument */
    void accept(T t);
}
複製代碼

常見的內置函數以下:數組

  • java.lang.Runnable 執行動做服務器

  • java.util.function.Predicate 接收T對象並返回boolean閉包

  • java.util.function.Consumer 接收T對象,不返回值app

  • java.util.function.Function<T,R> 接收T對象,返回R對象.函數式編程

  • java.util.function.Supplier 提供T對象(例如工廠),不接收值函數

  • 方法引用
    方法引用有不少種,它們的語法以下:

  • 靜態方法引用:ClassName::methodName

  • 實例上的實例方法引用:instanceReference::methodName

  • 超類上的實例方法引用:super::methodName

  • 類型上的實例方法引用:ClassName::methodName

  • 構造方法引用:Class::new

  • 數組構造方法引用:TypeName[]::new

@Test
    public void test_instance(){
        Set<String> girls = new HashSet<>();

        Set<String> names = new HashSet<>();
        names.stream()
                //實例::methodName
                .filter(girls::contains)
                .collect(Collectors.toList());
    }
    @Test
    public void test_this(){
        Set<String> names = new HashSet<>();
        names.stream()
                //this::methodName
                .filter(this::hasAuth)
                .collect(Collectors.toList());
    }


    private boolean hasAuth(String authKey){
        return true;
    }
複製代碼
  • 類型推斷
Map<String,Person> map = new HashMap<>();

    map.forEach((String name,Person person)->{
        person.fly();
        System.out.println(name+":"+person);
    });

    map.forEach((name,person)->{
        // 無須判斷類型,自動根據上下文推斷
        person.fly();
        System.out.println(name+":"+person);
    });
複製代碼

2. 實踐

  • 最佳實踐
    消滅代碼片斷
/** * 正常的代碼 */
    @Test
    public void test_person(){
        CnResult<Person> result = null;
        try {
            // 只有這裏取值是不一樣的,其餘的處理是同樣的,包括try,catch,打日誌,定義異常等
            Person entity = this.getPerson();

            result = CnResult.success(entity);

        }catch (CnException e){
            logger.error(e.getMessage(),e);
            result = CnResult.error(e.getErrorCode(),e.getMessage());

        }
        catch (Exception e) {
            logger.error(e.getMessage(),e);
            result = CnResult.error("1-1-1-1",e.getMessage());
        }
        Assert.assertNotNull(result);
    }

    @Test
    public void test_animal(){
        CnResult<Animal> result = null;
        try {
            // 只有這裏取值是不一樣的,其餘的處理是同樣的,包括try,catch,打日誌,定義異常等
            Animal entity = this.getAnimal();

            result = CnResult.success(entity);

        }catch (CnException e){
            logger.error(e.getMessage(),e);
            result = CnResult.error(e.getErrorCode(),e.getMessage());

        }
        catch (Exception e) {
            logger.error(e.getMessage(),e);
            result = CnResult.error("1-1-1-1",e.getMessage());
        }
        Assert.assertNotNull(result);
    }
    /** * lambda代碼 */
    @Test
    public void test_lambda(){
        //屏蔽全部細節,只把獲取對象的邏輯傳遞進去
        CnResult<Person> person = WapperUtils.wapper(this::getPerson);
        Assert.assertNotNull(person);

        CnResult<Animal> animal = WapperUtils.wapper(this::getAnimal);
        Assert.assertNotNull(animal);
    }


    public class WapperUtils {
        private static final Logger logger = LoggerFactory.getLogger(WapperUtils.class);
    
        /** * 包裹 * * @param supplier * @param <T> * @return */
        public static <T> CnResult<T> wapper(Supplier<T> supplier){
            try {
                T entity = supplier.get();
                CnResult<T> cnResult = CnResult.success(entity);
                return cnResult;
            }catch (CnException e){
                logger.error(e.getMessage(),e);
                return CnResult.error(e.getErrorCode(),e.getMessage());
            }
            catch (Exception e) {
                logger.error(e.getMessage(),e);
                return CnResult.error("1-1-1-1",e.getMessage());
            }
        }
    }
複製代碼

只要是代碼片斷,找到變點化,抽象成lambda,把固定的代碼統一封裝,就能夠使用行爲的複用,減小代碼片斷和代碼包裹。
明確語義

@Test
    public void test_first() {
        List<Person> persons = new ArrayList<>();
        persons.stream()
                /** * 沒有明確的語義。須要根據過程計算推理得出結果,也不清楚背後的業務和場景 * */
                .filter(person -> person.getAge() >= 18)
                .collect(Collectors.toList());
    }

    @Test
    public void test_second() {
        List<Person> persons = new ArrayList<>();
        persons.stream()
                .filter(person -> {
                    /** * 若是職責變動,得修改代碼結構,並且代碼愈來愈難理解 */
                    if (person.getAge() >= 18) {
                        return true;
                    }

                    if (person.getName().startsWith("莊")) {
                        return true;
                    }
                    return false;

                })
                .collect(Collectors.toList());
    }

    @Test
    public void test_third() {
        List<Person> persons = new ArrayList<>();
        persons.stream()
                /** * 隨着業務變動須要不斷添加filter */
                .filter(person -> person.getAge() >= 18)
                .filter(person -> person.getName().startsWith("莊"))
                .collect(Collectors.toList());
    }

    @Test
    public void test_fourth() {
        List<Person> persons = new ArrayList<>();
        persons.stream()
                /** * 隨着業務變動須要不斷添加filter */
                .filter(Person::isAdult)
                .filter(Person::belongToZhuang)
                .collect(Collectors.toList());
    }

    @Test
    public void test_final() {
        List<Person> persons = new ArrayList<>();
        /** * 有明確的語義,不用再面向細節加工一下,並且也方便後面的擴展 */

        persons.stream()
                .filter(Person::hasAuth)
                .collect(Collectors.toList());
    }
複製代碼
  • 最佳反例
    隨意取名
@Test
    public void test_name_bad(){
        List<Person> persons = new ArrayList<>();
        /** * lambda一多的時候,沒有明確的參數,計算和理解起來很是吃力。 */
        persons.stream()
                .filter(e->e.getAge()>18)
                .map(e->e.getName())
                .filter(e->e.startsWith("莊"))
                .collect(Collectors.toList());
    }
    @Test
    public void test_name(){
        List<Person> persons = new ArrayList<>();
        persons.stream()
                .filter(person->person.getAge()>18)
                .map(person->person.getName())
                .filter(name->name.startsWith("莊"))
                .collect(Collectors.toList());
    }
複製代碼

參數須要有明確語義
邏輯複雜

@Test
    public void test_bad() {
        List<Integer> values = new ArrayList<>();
        
        int result = values.stream().mapToInt(e -> {
            int sum = 0;
            for (int i = 0; i < e; i++) {
                if (e % i == 0) {
                    sum += i;
                }
            }
            return sum;
        }).sum();

        System.out.println(result);
    }

    @Test
    public void test_() {
        List<Integer> values = new ArrayList<>();

        int result = values.stream().mapToInt(this::toInt).sum();

        System.out.println(result);
    }

    private Integer toInt(int e) {
        int sum = 0;
        for (int i = 0; i < e; i++) {
            if (e % i == 0) {
                sum += i;
            }
        }
        return sum;
    }
複製代碼
  • 難以讀懂
  • 用途不明
  • 難以測試
  • 難以複用

建議把多行lambda表達式主體轉移到一個命名函數中,而後使用方法引用

  • 思考
    • 和內部類的區別
    • 和AOP的區別

好消息

騰訊雲如今作活動,新手福利1000減750,雲服務器最低2折,1核2G內存50G硬盤3年最低只要600元!戳此瞭解詳情!


轉自:https://www.cnblogs.com/ansn001/p/9399360.html

相關文章
相關標籤/搜索