1. 使用 Stream 簡化集合操作
2. 使用 Optional 簡化判空邏輯
空指針異常(NullPointerExceptions
)是 Java 最常見的異常之一宏榕,一直以來都困擾著 Java 程序員柿估。一方面即舌,程序員不得不在代碼中寫很多null
的檢查邏輯拼岳,讓代碼看起來非常臃腫;另一方面窘游,由于其屬于運行時異常,是非常難以預(yù)判的跳纳。
為了預(yù)防空指針異常忍饰,Google
的Guava
項目率先引入了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);
}
注: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ù)式接口
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一樣灾常,基本不消耗性能
優(yōu)先推薦使用MapStruct,其次是org.springframework.beans中的 BeanUtils铃拇;