亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

編譯時的泛型重載

編譯時的泛型重載

楊__羊羊 2022-06-30 11:41:53
是否可以設計一種在編譯時調用不同方法重載的方法?可以說,我有這個小班:@RequiredArgsConstructorpublic class BaseValidator<T> {    private final T newValue;}現在,我需要返回不同對象的方法(取決于T)。像這樣:private StringValidator getValidator() {    return new ValidationString(newValue);}private IntegerValidator getValidator() {    return new Validation(newValue);}最后,我想要一個非常流暢的調用層次結構,看起來像這樣:new BaseValidator("string")    .getValidator() // which returns now at compile-time a StringValidator    .checkIsNotEmpty();//ornew BaseValidator(43)    .getValidator() // which returns now a IntegerValidator    .checkIsBiggerThan(42);在我的“真實”案例中(我有一種非常具體的方法來更新對象和每個對象的很多條件,并且復制和粘貼問題的可能性非常高。所以向導強制所有開發人員實施精確這邊走。) : 理想圖像我嘗試了不同的方法。驗證器中的復雜泛型,或使用泛型。我的最后一個方法看起來像這樣。public <C> C getValidator() {    return (C) getValidation(newValue);}private ValidationString getValidation(String newValue) {    return new StringValidator(newValue);}private ValidationInteger getValidation(Integer newValue) {    return new IntegerValidation(newValue);}訣竅是什么?//編輯:我希望它在編譯時而不是instanceof在運行時使用 -checks。
查看完整描述

2 回答

?
慕蓋茨4494581

TA貢獻1850條經驗 獲得超11個贊

訣竅是什么?


不要這樣做。


提供靜態工廠方法:


class BaseValidator<T> {

  static ValidationString getValidation(String newValue) {

    return new ValidationString(newValue);

  }


  static ValidationInteger getValidation(Integer newValue) {

    return new ValidationInteger(newValue);

  }

}


class ValidationString extends BaseValidator<String> { ... }

class ValidationInteger extends BaseValidator<Integer> { ... }

盡管我認為這很奇怪:您指的是基類中的子類。這種循環依賴使代碼難以使用,尤其是在重構時,但也可能在初始化時。


相反,我建議創建一個實用程序類來包含工廠方法:


class Validators {

  private Validators() {}


  static ValidationString getValidation(String newValue) {

    return new ValidationString(newValue);

  }


  static ValidationInteger getValidation(Integer newValue) {

    return new ValidationInteger(newValue);

  }

}

沒有這樣的循環。


關于泛型,需要意識到的一件非常重要的事情是,它只不過是使顯式強制轉換為隱式(然后檢查所有這些隱式強制轉換是否是類型安全的)。


換句話說,這:


List<String> list = new ArrayList<>();

list.add("foo");

System.out.println(list.get(0).length());

只是一種更好的寫作方式:


List list = new ArrayList();

list.add((String) "foo");

System.out.println(((String) list.get(0)).length());

雖然<String>看起來它是類型的一部分,但它基本上只是對編譯器的一條指令,用于噴射大量強制轉換。


具有不同類型參數的泛型類都具有相同的方法。這是您的方法中的具體困難:您不能BaseValidator<String>.getValidator()用checkIsNotEmpty方法(僅)BaseValidator<Integer>.getValidator()返回某些東西,而用方法(僅)返回某些東西checkIsGreaterThan。


好吧,說你不能,這并不完全正確。在您嘗試涉及方法范圍的類型變量 ( <C> C getValidator()) 時,您可以編寫:


new BaseValidator<>("string").<StringValidator>getValidator().checkIsNotEmpty()

(假設上面StringValidator有checkIsNotEmpty方法)


但:


讓我們不要拐彎抹角:它是丑陋的。

比丑陋更糟糕的是,它不是類型安全的。你同樣可以寫:


新 BaseValidator<>("string").getValidator().checkIsGreaterThan(42)


這是荒謬的,但編譯器允許。問題是在調用站點選擇了返回類型:您要么必須返回 null (并NullPointerException在您嘗試調用以下方法時得到 a );或返回一些非空值并冒險 a ClassCastException。無論哪種方式:不好。


但是,您可以做的是使通用驗證器成為方法調用的參數。例如:


interface Validator<T> {

  void validate(T b);

}


class BaseValidator<T> {

  BaseValidator<T> validate(Validator<T> v) {

    v.validate(this.value);

  }

}

并像這樣調用,演示如何鏈接方法調用以應用多個驗證:


new BaseValidator<>("")

    .validate(s -> !s.isEmpty())

    .validate(s -> s.matches("pattern"))

    ...


new BaseValidator<>(123)

    .validate(v -> v >= 0)

    ...


查看完整回答
反對 回復 2022-06-30
?
慕田峪9158850

TA貢獻1794條經驗 獲得超8個贊

我們決定添加更多的類步驟。您可以采用通用方式或使用顯式類型的方式(在此示例中,String)。我們對所有更新方法的要求(我們有許多數據庫對象......)有點復雜。我們只想要一個更新方法(對于每個數據庫對象),它...

  1. 忽略字段,即為空。

  2. 忽略等于“舊”值的字段。

  3. 驗證未被忽略的字段。

  4. 僅在沒有發生驗證問題時保存。

用許多 if 塊來做到這一點是可能的,但不是真正可讀的。并且復制粘貼失敗的可能性很高。

我們的代碼如下所示:

private void update(@NonNull final User.UpdateFinalStep params) {


    UpdateWizard.update(dbUserService.get(params.getId())


            .field(params.getStatus())

            .withGetter(DbUser::getAccountStatus)

            .withSetter(DbUser::setAccountStatus)

            .finishField()


            .field(Optional.ofNullable(params.getUsername())

               .map(String::toLowerCase)

               .orElse(null))

            .withGetter(DbUser::getUsername)

            .withSetter(DbUser::setUsername)

            .beginValidationOfField(FieldName.USERNAME)

            .notEmptyAndMatchPattern(USERNAME_PATTERN, () -> this.checkUniqueUsername(params.getUsername(), params.getId()))

            .endValidation()


            .field(params.getLastName())

            .withGetter(DbUser::getLastname)

            .withSetter(DbUser::setLastname)

            .beginValidationOfField(FieldName.USER_LASTNAME)

            .notEmptyAndMatchPattern(LAST_NAME_PATTERN)

            .endValidation()


            .field(params.getFirstName())

            .withGetter(DbUser::getFirstname)

            .withSetter(DbUser::setFirstname)

            .beginValidationOfField(FieldName.USER_FIRSTNAME)

            .notEmptyAndMatchPattern(FIRST_NAME_PATTERN)

            .endValidation()


            .save(dbUserService::save);

}

這是非常易讀的,并且允許以非常簡單的方式添加新字段。使用泛型,我們不會給“愚蠢的開發者”犯錯的機會。


如圖所示,accountStatus 和 username 指向不同的類。

http://img1.sycdn.imooc.com//62bd1bb10001285711100425.jpg

最后,我們可以非常流暢地使用更新方法:

userService.startUpdate()
   .withId(currentUserId)
   .setStatus(AccountStatus.INACTIVE)
   .finallyUpdate();


查看完整回答
反對 回復 2022-06-30
  • 2 回答
  • 0 關注
  • 122 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號