Java8 優(yōu)雅簡化代碼

1. 使用 Stream 簡化集合操作

Java8 Stream流操作總結(jié)

2. 使用 Optional 簡化判空邏輯

空指針異常(NullPointerExceptions)是 Java 最常見的異常之一宏榕,一直以來都困擾著 Java 程序員柿估。一方面即舌,程序員不得不在代碼中寫很多null的檢查邏輯拼岳,讓代碼看起來非常臃腫;另一方面窘游,由于其屬于運行時異常,是非常難以預(yù)判的跳纳。

為了預(yù)防空指針異常忍饰,GoogleGuava項目率先引入了Optional類,通過使用檢查空值的方式來防止代碼污染寺庄,受到Guava項目的啟發(fā)艾蓝,隨后在Java 8中也引入了Optional類力崇。

Optional 類位于java.util包下,是一個可以為 null容器對象赢织,如果值存在則isPresent()方法會返回 true 亮靴,調(diào)用 get() 方法會返回該對象,可以有效避免空指針異常于置。

2.1 創(chuàng)建Optional對象

empty()茧吊、of()、ofNullable()

//可以使用靜態(tài)方法 empty() 創(chuàng)建一個空的 Optional 對象
Optional<String> empty = Optional.empty();
System.out.println(empty); // 輸出:Optional.empty

//可以使用靜態(tài)方法 of() 創(chuàng)建一個非空的 Optional 對象
Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt); // 輸出:Optional[沉默王二]

//當(dāng)然了八毯,傳遞給 of() 方法的參數(shù)必須是非空的搓侄,也就是說不能為 null,否則仍然會拋出 NullPointerException话速。
String name = null;
Optional<String> optnull = Optional.of(name);

//可以使用靜態(tài)方法 ofNullable() 創(chuàng)建一個即可空又可非空的 Optional 對象
String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // 輸出:Optional.empty

2.2 判斷值是否存在

isPresent()讶踪; isEmpty()

//可以通過方法 isPresent() 判斷一個 Optional 對象是否存在,如果存在泊交,該方法返回 true乳讥,否則返回 false——取代了 obj != null 的判斷。
Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt.isPresent()); // 輸出:true

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 輸出:false


//Java 11 后還可以通過方法 isEmpty() 判斷與 isPresent() 相反的結(jié)果廓俭。

Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt.isPresent()); // 輸出:false

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 輸出:true

2.3 非空表達(dá)式

ifPresent()雏婶,允許我們使用函數(shù)式編程的方式執(zhí)行一些代碼,因此白指,我把它稱為非空表達(dá)式留晚。如果沒有該方法的話,我們通常需要先通過 isPresent() 方法對 Optional 對象進(jìn)行判空后再執(zhí)行相應(yīng)的代碼:

Optional<String> optOrNull = Optional.ofNullable(null);
if (optOrNull.isPresent()) {
    System.out.println(optOrNull.get().length());
}

有了 ifPresent() 之后告嘲,情況就完全不同了错维,可以直接將 Lambda 表達(dá)式傳遞給該方法,代碼更加簡潔橄唬,更加直觀赋焕。

Optional<String> opt = Optional.of("沉默王二");
opt.ifPresent(str -> System.out.println(str.length()));

//Java 9 后還可以通過方法 ifPresentOrElse(action, emptyAction) 執(zhí)行兩種結(jié)果,非空時執(zhí)行 action仰楚,空時執(zhí)行 emptyAction隆判。

Optional<String> opt = Optional.of("沉默王二");
opt.ifPresentOrElse(str -> System.out.println(str.length()), () -> System.out.println("為空"));

2.4 設(shè)置(獲取)默認(rèn)值

有時候僧界,我們在創(chuàng)建(獲惹揉帧) Optional 對象的時候,需要一個默認(rèn)值捂襟,orElse() 和 orElseGet() 方法就派上用場了咬腕。

orElse() 方法用于返回包裹在 Optional 對象中的值,如果該值不為 null葬荷,則返回涨共;否則返回默認(rèn)值纽帖。該方法的參數(shù)類型和值得類型一致。

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("沉默王二");
System.out.println(name); // 輸出:沉默王二

orElseGet() 方法與 orElse() 方法類似举反,但參數(shù)類型不同懊直。如果 Optional 對象中的值為 null,則執(zhí)行參數(shù)中的函數(shù)火鼻。

String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()->"沉默王二");
System.out.println(name); // 輸出:沉默王二

orElseGet性能更好一點室囊,因為無論Optional 對象中的值是不是為空,orElse都會執(zhí)行凝危,只是不返回而已波俄。但是通常這里的邏輯不會很復(fù)雜,所以影響不大蛾默。

2.5 獲取值

直觀從語義上來看懦铺,get() 方法才是最正宗的獲取 Optional 對象值的方法,但很遺憾支鸡,該方法是有缺陷的冬念,因為假如 Optional 對象的值為 null,該方法會拋出 NoSuchElementException 異常牧挣。這完全與我們使用 Optional 類的初衷相悖急前。

public class GetOptionalDemo {
    public static void main(String[] args) {
        String name = null;
        Optional<String> optOrNull = Optional.ofNullable(name);
        System.out.println(optOrNull.get());
    }
}

建議 orElseGet() 方法獲取 Optional 對象的值。

2.6 filter 瀑构、map

  • filter() 方法的參數(shù)類型為 Predicate(Java 8 新增的一個函數(shù)式接口)裆针,也就是說可以將一個 Lambda 表達(dá)式傳遞給該方法作為條件,如果表達(dá)式的結(jié)果為 false寺晌,則返回一個 EMPTY 的 Optional 對象世吨,否則返回過濾后的 Optional 對象。

  • map() 方法呻征,該方法可以按照一定的規(guī)則將原有 Optional 對象轉(zhuǎn)換為一個新的 Optional 對象耘婚,原有的 Optional 對象不會更改。

public class OptionalMapFilterDemo {
    public static void main(String[] args) {
        String password = "password";
        Optional<String>  opt = Optional.ofNullable(password);

        Predicate<String> len6 = pwd -> pwd.length() > 6;
        Predicate<String> len10 = pwd -> pwd.length() < 10;
        Predicate<String> eq = pwd -> pwd.equals("password");
    
        boolean result = opt.map(String::toLowerCase).filter(len6.and(len10 ).and(eq)).isPresent();
        System.out.println(result);
    }
}

總結(jié):使用 Optional陆赋,不僅可以避免使用 Stream 進(jìn)行級聯(lián)調(diào)用的空指針問題沐祷;更重要的是,它提供了一些實用的方法幫我們避免判空邏輯攒岛。

如下是一些例子赖临,演示了如何使用 Optional 來避免空指針,以及如何使用它的 fluent API 簡化冗長的 if-else 判空邏輯

@Test(expected = IllegalArgumentException.class)
public void optional() { 
    //通過get方法獲取Optional中的實際值阵子,很少用到
    assertThat(Optional.of(1).get(), is(1));
    //通過ofNullable來初始化一個null思杯,通過orElse方法實現(xiàn)Optional中無數(shù)據(jù)的時候返回一個默認(rèn)值         
    assertThat(Optional.ofNullable(null).orElse("A"), is("A"));    
    //OptionalDouble是基本類型double的Optional對象,isPresent判斷有無數(shù)據(jù)
    assertFalse(OptionalDouble.empty().isPresent());
    //通過map方法可以對Optional對象進(jìn)行級聯(lián)轉(zhuǎn)換挠进,不會出現(xiàn)空指針色乾,轉(zhuǎn)換后還是一個Optional
    assertThat(Optional.of(1).map(Math::incrementExact).get(), is(2));
    //通過filter實現(xiàn)Optional中數(shù)據(jù)的過濾,得到一個Optional领突,然后級聯(lián)使用orElse提供默認(rèn)值
    assertThat(Optional.of(1).filter(integer -> integer % 2 == 0).orElse(null), is(nullValue()));
    //通過orElseThrow實現(xiàn)無數(shù)據(jù)時拋出異常
    Optional.empty().orElseThrow(IllegalArgumentException::new);
}
image.png

注:Optional 判空的生成應(yīng)用見 Java程序中空值/異常最佳實踐

3. Map接口 Map接口 putIfAbsent / computeIfAbsent/computeIfPresent

3.1 putIfAbsent

如果給定的key不存在(或者key對應(yīng)的value為null)暖璧,關(guān)聯(lián)給定的key和給定的value,并返回null君旦;如果存在澎办,返回當(dāng)前值(不會把value放進(jìn)去);

使用場景:如果我們要變更某個key的值金砍,我們又不知道key是否存在的情況下局蚀,而又不希望增加key的情況使用。

demo:

// 創(chuàng)建一個 HashMap
HashMap<Integer, String> sites = new HashMap<>();

// 往 HashMap 添加一些元素
sites.put(1, "Google");
sites.put(2, "Runoob");
sites.put(3, "Taobao");
System.out.println("sites HashMap: " + sites);

// HashMap 不存在該key
sites.putIfAbsent(4, "Weibo");

// HashMap 中存在 Key
sites.putIfAbsent(2, "Wiki");
System.out.println("Updated Languages: " + sites);

//結(jié)果為
sites HashMap: {1=Google, 2=Runoob, 3=Taobao}
Updated sites HashMap: {1=Google, 2=Runoob, 3=Taobao, 4=Weibo}

3.2 computeIfAbsent

如果給定的 key 不存在(或者 key 對應(yīng)的 value 為 null)恕稠,就去計算 mappingFunction 的值琅绅;

如果 mappingFunction 的值不為 null,就把 key = value 放進(jìn)去鹅巍;
如果 mappingFunction 的值為 null千扶,就不會記錄該映射關(guān)系,返回值為 null骆捧;
如果計算 mappingFunction 的值的過程出現(xiàn)異常澎羞,再次拋出異常,不記錄映射關(guān)系敛苇,返回 null妆绞;

如果存在該 key,返回的是新的 value枫攀,就算 key 對應(yīng)的 value 部位 null括饶,返回 null;(HashMap 返回的是舊 value)

default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
map.computeIfAbsent(key, k -> new Value(f(k)));
map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);

demo:

public void test() {
    Map<String, List<String>> map = new HashMap<>();
    // 如果key不存在脓豪,則創(chuàng)建新list并放入數(shù)據(jù)巷帝;key存在,則直接往list放入數(shù)據(jù)
    map.computeIfAbsent("fruit", k -> new ArrayList<>()).add("apple");
    map.computeIfAbsent("fruit", k -> new ArrayList<>()).add("orange");
    map.computeIfAbsent("language", k -> new ArrayList<>()).add("english");
    // 遍歷
    map.forEach((k, v) -> System.out.println(k + " " + v));
}

map.computeIfAbsent("fruit", k -> new ArrayList<>()).add("apple");
這一句的意思是
key為fruit的鍵值對是否存在
1扫夜、如果存在楞泼,則給當(dāng)前操作的List集合添加 apple
2、如果不存在笤闯,則新創(chuàng)建List結(jié)構(gòu)堕阔,將apple添加到List集合,再存入Map集合

putIfAbsent 和 computeIfAbsent

  • 都是在 key 不存在的時候才會建立 key 和 value 的映射關(guān)系颗味;
  • putIfAbset 不論傳入的 value 是否為空超陆,都會建立映射(并不適合所有子類,例如 HashTable),而computeIfAbsent 方法时呀,當(dāng)存入value為空時张漂,不做任何操作
  • putIfAbsent返回的是舊value,當(dāng)key不存在時返回null谨娜;computeIfAbsent返回的都是新的value航攒,即使computeIfAbsent在傳入的value為null時,不會新建映射關(guān)系趴梢,但返回的也是null漠畜;

3.3 computeIfPresent

key 存在并且不為空,計算 remappingFunction 的值 value坞靶;

如果 value 不為空憔狞,保存指定 key 和 value 的映射關(guān)系;
如果 value 為 null彰阴,remove(key)瘾敢;
如果計算 value 的過程拋出了異常,computeIfPresent 方法中會再次拋出硝枉,key 和其對應(yīng)的值不會改變

default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

demo:

 // 創(chuàng)建一個 HashMap
 HashMap<String, Integer> prices = new HashMap<>();

// 往HashMap中添加映射關(guān)系
prices.put("Shoes", 200);
prices.put("Bag", 300);
prices.put("Pant", 150);
System.out.println("HashMap: " + prices);

// 重新計算鞋加上10%的增值稅后的價值
int shoesPrice = prices.computeIfPresent("Shoes", (key, value) -> value + value * 10/100);
System.out.println("Price of Shoes after VAT: " + shoesPrice);

// 輸出更新后的HashMap
System.out.println("Updated HashMap: " + prices);


執(zhí)行以上程序輸出結(jié)果為:
HashMap: {Pant=150, Bag=300, Shoes=200}
Price of Shoes after VAT: 220
Updated HashMap: {Pant=150, Bag=300, Shoes=220}}

computeIfPresent 和 computeIfAbsent

  • 這兩個方法正好相反廉丽,computeIfPresent 在key存在時,才會用新的value替換oldValue

  • 當(dāng)傳入的key存在妻味,并且傳入的value為null時正压,computeIfPresent 會remove(key),把傳入的key對應(yīng)的映射關(guān)系移除责球;而computeIfAbsent不論何時都不會remove()焦履;

  • computeIfPresent 只有在key存在,并且傳入的value不為空的時候雏逾,返回值是value嘉裤,其他情況都是返回null;

    computeIfAbsent 只有在key不存在栖博,并且傳入的value不為null的時候才會返回value屑宠,其他情況都返回null;

4. 函數(shù)式接口 Lambda 表達(dá)式

函數(shù)式接口是一種只有單一抽象方法的接口仇让,使用 @FunctionalInterface 來描述典奉,可以隱式地轉(zhuǎn)換成 Lambda 表達(dá)式。使用 Lambda 表達(dá)式來實現(xiàn)函數(shù)式接口丧叽,不需要提供類名和方法定義卫玖,通過一行代碼提供函數(shù)式接口的實例,就可以讓函數(shù)成為程序中的頭等公民踊淳,可以像普通數(shù)據(jù)一樣作為參數(shù)傳遞假瞬,而不是作為一個固定的類中的固定方法。

那,函數(shù)式接口到底是什么樣的呢脱茉?java.util.function 包中定義了各種函數(shù)式接口剪芥。

比如,用于提供數(shù)據(jù)的 Supplier 接口芦劣,就只有一個 get 抽象方法粗俱,沒有任何入?yún)⑺涤堋⒂幸粋€返回值:

@FunctionalInterfacepublic interface Supplier { 
    /** * Gets a result. * * @return a result */
    T get();
}

我們可以使用 Lambda 表達(dá)式或方法引用虚吟,來得到 Supplier 接口的實例:

//使用Lambda表達(dá)式提供Supplier接口實現(xiàn),返回OK字符串
Supplier stringSupplier = ()->"OK";

//使用方法引用提供Supplier接口實現(xiàn)签财,返回空字符串
Supplier supplier = String::new;

4.1 四大基本函數(shù)式接口

Function<T, R>
接受一個入?yún)串慰,返回R類型對象,使用apply方法獲取方法執(zhí)行的內(nèi)容

R apply(T t);

demo1:

//Function接口是輸入一個數(shù)據(jù)唱蒸,計算后輸出一個數(shù)據(jù)邦鲫。我們先把字符串轉(zhuǎn)換為大寫,然后通過andThen組合另一個Function實現(xiàn)字符串拼接
Function<String, String> upperCase = String::toUpperCase;
Function<String, String> duplicate = s -> s.concat(s);
upperCase.andThen(duplicate).apply("test");

demo2:

User user = new User(88, "bb");

String name = uft.apply(user);
System.out.println(name);


/**
* Function<T, R> lambda寫法
*/
private static Function<User, String> uft = u -> u.getName();

Consumer<T>
接受一個參數(shù)T神汹,沒有返回值庆捺,使用accept方法對參數(shù)執(zhí)行操作

oid accept(T t);

demo1:

//Consumer接口是消費一個數(shù)據(jù)。我們通過andThen方法組合調(diào)用兩個Consumer屁魏,輸出兩行abcdefg
Consumer<String> println = System.out::println;
println.andThen(println).accept("abcdefg");

demo2:

User user = new User(88, "bb");

uc.accept(user);

/**
* Consumer<T> lambda寫法
*/
private static Consumer<User> uc = u -> System.out.println(u.getName());

Supplier<T>
沒有入?yún)⑻弦裕祷豑類型結(jié)果,使用get方法獲取返回結(jié)果

T get();

demo1:

//Supplier是提供一個數(shù)據(jù)的接口氓拼。這里我們實現(xiàn)獲取一個隨機(jī)數(shù)
Supplier<Integer> random = ()->ThreadLocalRandom.current().nextInt();
System.out.println(random.get());

demo2:

User user1 = us.get();
System.out.println(user1.getName());

/**
* Supplier<T> lambda寫法
*/
private static Supplier<User> us = () -> new User(1, "us");

Predicate<T>
接受一個入?yún)⒛慊祷亟Y(jié)果為true或者false,使用test方法進(jìn)行測試并返回測試結(jié)果

boolean test(T t);

demo1:

//Predicate接口是輸入一個參數(shù),返回布爾值桃漾。我們通過and方法組合兩個Predicate條件坏匪,判斷是否值大于0并且是偶數(shù)
Predicate<Integer> positiveNumber = i -> i > 0;
Predicate<Integer> evenNumber = i -> i % 2 == 0;
assertTrue(positiveNumber.and(evenNumber).test(2));

demo2:

boolean test = up.test(user);
System.out.println(test);    

/**
* Predicate<T>
*/
private static Predicate<User> up = u -> !u.getName().isEmpty();

4.2 自定義函數(shù)式接口

參見 Java 函數(shù)式編程實例

5 實體轉(zhuǎn)換工具M(jìn)apstruct

MapStruct是一個代碼生成器,簡化了不同的Java Bean之間映射的處理撬统,所以映射指的就是從一個實體變化成一個實體适滓。例如我們在實際開發(fā)中,DAO層的實體和一些數(shù)據(jù)傳輸對象(DTO)恋追,大部分屬性都是相同的凭迹,只有少部分的不同,通過mapStruct几于,可以讓不同實體之間的轉(zhuǎn)換變的簡單蕊苗。我們只需要按照約定的方式進(jìn)行配置即可。

1.BeanUtils原理:反射沿彭,是在運行階段朽砰,至于反射為什么慢,后續(xù)我了解再補(bǔ)充;

2.MapStruct原理:在編譯階段生產(chǎn)get瞧柔、set方法漆弄,就跟我們自己寫get、set一樣造锅,基本不消耗性能撼唾;

MapStruct的優(yōu)點

  • 相比于手動get、set哥蔚,無需手寫轉(zhuǎn)換工具類倒谷,減輕大量的體力活

  • 效率高
    核心的轉(zhuǎn)換邏輯并不是通過反射實現(xiàn),而是通過編譯時自動生成基于getter/setter 轉(zhuǎn)換實現(xiàn)類渤愁;

  • 性能高
    基于簡單的get、set操作深夯,效率達(dá)到最佳

  • 編譯時類型安全
    只能映射相同名稱或帶映射標(biāo)記的屬性;

  • 編譯時產(chǎn)生錯誤報告
    如果映射不完整或映射不正確則會在編譯時拋出異常咕晋,代碼將無法正常運行呢撞;

  • 能明確查看轉(zhuǎn)換的細(xì)節(jié)
    編譯生成的class對象可以看到詳細(xì)的轉(zhuǎn)換過程饰剥,方便快速定位轉(zhuǎn)換過程中的問題殊霞。

5.1 入門

  • 引入jar
  <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>1.4.1.Final</version>
        </dependency>
  • 實體類
    實體類是開發(fā)過程少不了的,就算是用工具生成肯定也是要有的汰蓉;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private String name;
    private int age;
    private GenderEnum gender;
    private Double height;
    private Date birthday;

}
public enum GenderEnum {
    Male("1", "男"),
    Female("0", "女");

    private String code;
    private String name;

    public String getCode() {
        return this.code;
    }

    public String getName() {
        return this.name;
    }

    GenderEnum(String code, String name) {
        this.code = code;
        this.name = name;
    }
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
    private String name;
    private int age;
    private String gender;
    private Double height;
    private String birthday;
}

  • 創(chuàng)建轉(zhuǎn)換Mapper
    需要手寫的部分就是這個Mapper的接口绷蹲,編譯完成后會自動生成相應(yīng)的實現(xiàn)類
@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "gender.name", target = "gender")
    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    StudentVO student2StudentVO(Student student);

}
  • test demo
public class Test {

    public static void main(String[] args) {

        Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();
        System.out.println(student);
        //這行代碼便是實際要用的代碼
        StudentVO studentVO = StudentMapper.INSTANCE.student2StudentVO(student);
        System.out.println(studentVO);

    }

}

mapper可以進(jìn)行字段映射,改變字段類型顾孽,指定格式化的方式祝钢,包括一些日期的默認(rèn)處理。

也可以手動指定格式化的方法:

@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "gender", target = "gender")
    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    StudentVO student2StudentVO(Student student);

    default String getGenderName(GenderEnum gender) {
        return gender.getName();
    }

}

5.2 List 轉(zhuǎn)換

@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "gender.name", target = "gender")
    @Mapping(source = "birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    StudentVO student2StudentVO(Student student);


    List<StudentVO> students2StudentVOs(List<Student> studentList);

}
public static void main(String[] args) {

    Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();

    List<Student> list = new ArrayList<>();
    list.add(student);
    List<StudentVO> result = StudentMapper.INSTANCE.students2StudentVOs(list);
    System.out.println(result);
}

5.3 多對象轉(zhuǎn)換到一個對象

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Student {

    private String name;
    private int age;
    private GenderEnum gender;
    private Double height;
    private Date birthday;

}
@Data
@AllArgsConstructor
@Builder
@NoArgsConstructor
public class Course {

    private String courseName;
    private int sortNo;
    private long id;

}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class StudentVO {
    private String name;
    private int age;
    private String gender;
    private Double height;
    private String birthday;
    private String course;
}
@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "student.gender.name", target = "gender")
    @Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(source = "course.courseName", target = "course")
    StudentVO studentAndCourse2StudentVO(Student student, Course course);

}
public class Test {

    public static void main(String[] args) {

        Student student = Student.builder().name("小明").age(6).gender(GenderEnum.Male).height(121.1).birthday(new Date()).build();
        Course course = Course.builder().id(1L).courseName("語文").build();

        StudentVO studentVO = StudentMapper.INSTANCE.studentAndCourse2StudentVO(student, course);
        System.out.println(studentVO);
    }

}

5.4 默認(rèn)值

@Mapper
public interface StudentMapper {

    StudentMapper INSTANCE = Mappers.getMapper(StudentMapper.class);

    @Mapping(source = "student.gender.name", target = "gender")
    @Mapping(source = "student.birthday", target = "birthday", dateFormat = "yyyy-MM-dd HH:mm:ss")
    @Mapping(source = "course.courseName", target = "course")
    @Mapping(target = "name", source = "student.name", defaultValue = "張三")
    StudentVO studentAndCourse2StudentVO(Student student, Course course);

}

5.5 性能

  • 1.BeanUtils原理:反射若厚,是在運行階段拦英。
  • 2.MapStruct原理:在編譯階段生產(chǎn)get、set方法测秸,就跟我們自己寫get疤估、set一樣灾常,基本不消耗性能
image.png

優(yōu)先推薦使用MapStruct,其次是org.springframework.beans中的 BeanUtils铃拇;

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末钞瀑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子慷荔,更是在濱河造成了極大的恐慌雕什,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,331評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件显晶,死亡現(xiàn)場離奇詭異贷岸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)吧碾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評論 3 398
  • 文/潘曉璐 我一進(jìn)店門凰盔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人倦春,你說我怎么就攤上這事÷浼簦” “怎么了睁本?”我有些...
    開封第一講書人閱讀 167,755評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長忠怖。 經(jīng)常有香客問我呢堰,道長,這世上最難降的妖魔是什么凡泣? 我笑而不...
    開封第一講書人閱讀 59,528評論 1 296
  • 正文 為了忘掉前任枉疼,我火速辦了婚禮,結(jié)果婚禮上鞋拟,老公的妹妹穿的比我還像新娘骂维。我一直安慰自己,他們只是感情好贺纲,可當(dāng)我...
    茶點故事閱讀 68,526評論 6 397
  • 文/花漫 我一把揭開白布航闺。 她就那樣靜靜地躺著,像睡著了一般猴誊。 火紅的嫁衣襯著肌膚如雪潦刃。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,166評論 1 308
  • 那天懈叹,我揣著相機(jī)與錄音乖杠,去河邊找鬼。 笑死澄成,一個胖子當(dāng)著我的面吹牛胧洒,可吹牛的內(nèi)容都是我干的笆包。 我是一名探鬼主播,決...
    沈念sama閱讀 40,768評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼略荡,長吁一口氣:“原來是場噩夢啊……” “哼庵佣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汛兜,我...
    開封第一講書人閱讀 39,664評論 0 276
  • 序言:老撾萬榮一對情侶失蹤巴粪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后粥谬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肛根,經(jīng)...
    沈念sama閱讀 46,205評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,290評論 3 340
  • 正文 我和宋清朗相戀三年漏策,在試婚紗的時候發(fā)現(xiàn)自己被綠了派哲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,435評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡掺喻,死狀恐怖芭届,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情感耙,我是刑警寧澤褂乍,帶...
    沈念sama閱讀 36,126評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站即硼,受9級特大地震影響逃片,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜只酥,卻給世界環(huán)境...
    茶點故事閱讀 41,804評論 3 333
  • 文/蒙蒙 一褥实、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧裂允,春花似錦损离、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至瓮增,卻和暖如春怎棱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背绷跑。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工拳恋, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人砸捏。 一個月前我還...
    沈念sama閱讀 48,818評論 3 376
  • 正文 我出身青樓谬运,卻偏偏與公主長得像隙赁,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子梆暖,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,442評論 2 359

推薦閱讀更多精彩內(nèi)容

  • 本文主要總結(jié)了《Java8實戰(zhàn)》伞访,適用于學(xué)習(xí) Java8 的同學(xué),也可以作為一個 API 手冊文檔適用轰驳,平時使用時...
    _曉__閱讀 1,183評論 1 6
  • 以下是《Java8簡明教程》的正文厚掷。 “Java并沒有沒落,人們很快就會發(fā)現(xiàn)這一點” 歡迎閱讀我編寫的Java 8...
    猿學(xué)閱讀 567評論 0 0
  • 以下內(nèi)容均為翻譯內(nèi)容级解,原文地址冒黑、示例代碼 歡迎閱讀 Java 8新特性介紹. 該篇問斬將一一介紹所有關(guān)于Java8...
    史蒂文哦閱讀 304評論 0 1
  • 一、Java8 的三個編程概念 流處理從輸入流中一個一個讀取數(shù)據(jù)項勤哗,然后以同樣的方式將數(shù)據(jù)項寫入輸出流抡爹。 用行為參...
    大棋17閱讀 1,341評論 0 0
  • 本文章轉(zhuǎn)載出處來自民工哥的總結(jié),鏈接地址: 民工哥之路[https://mp.weixin.qq.com/s/mO...
    kGk_f44f閱讀 265評論 0 0