Kotlin Sealed Class 太香了,Java 8 也想用怎么办?

为防止数据在分发进程中被歹意篡改,Kotlin 将 SealedClass 参数设置为 val 即可,

Java 17 以下未引入 SealedClass,且若完成 Kotlin val 同等效果,样板代码瞬间飙出许多,等于处理了数据一致性的同时,滋生了更多 “不一致” 问题,例如日后修改某字段,而忘配置结构办法等等。

痛定思痛,SealedClass4Java 应运而生,经过注解主动生成 SealedClass,像 Kotlin 相同运用 SealedClass。

献给喜爱 Kotlin 但又不得不维护 Java 老项目的朋友。

Github:SealedClass4Java

运用说明

1.创建一个接口,增加 SealedClass 注解,且接口名最初 _ 下划线,

@SealedClass
public interface _TestEvent {
 void resultTest1(String a, int b);
 void resultTest2(String a, int b, int c);
}

2.编译即可生成目标类,例如 TestEvent,然后像 Kotlin 相同运用该类:

TestEvent event = TestEvent.ResultTest1("textx");
switch (event.id) {
 case TestEvent.ResultTest1.ID:
  TestEvent.ResultTest1 event1 = (TestEvent.ResultTest1) event;
  event1.copy(1);
  event1.paramA;
  event1.resultB;
  break;
 case TestEvent.ResultTest2.ID:
  break;
}

进阶运用

本框架是 MVI-Dispatcher 项目优化进程中,为消除 “音讯分流场景 final 样板代码” 而萌生的产物,所以咱们不妨以 MVI-Dispatcher 运用场景为例:

注:“音讯(message)、事情(event)、目的(intent)”,不同场景,叫法不同,但本质上是指同一东西,即 “可被消费的一次性数据”。

A.朴实音讯分发场景

1.界说一个接口,例如 _Messages,在办法列表中界说不带着参数的朴实音讯,界说完 build 生成对应 Messages 类。

@SealedClass
public interface _Messages {
 void refreshNoteList();
 void finishActivity();
}

2.在 MVI-View 中发送一个 Messages.RefreshNoteList( ) 朴实音讯

public class TestFragment {
 public void onInput() {
  MVI-Model.input(Messages.RefreshNoteList());
  }
}

3.在 MVI-Model 中转发音讯

public class PageMessenger extends MVI-Disptacher {
  protected void onHandle(Messages intent){
    sendResult(intent);
  }
}

4.在 MVI-View 中呼应音讯

public class TestFragment {
 public void onOutput() {
  MVI-Model.output(this, intent-> {
     switch(intent.id) {
       case Messages.RefreshNoteList.ID: ... break;
       case Messages.FinishActivity.ID: ... break;
     }
   });
  }
}

B.带参数的目的分发场景

该场景非常普遍,例如页面向后台请求一数据,经过目的来传递参数,后台处理好成果,将成果注入到目的中,回传给页面。

所以该场景下,目的会带着 “参数” 和 “成果”,且发送场景下只需注入参数,回推场景下只需注入成果,

因此运用办法即,

1.界说接口,为参数增加 @Param 注解,

@SealedClass
public interface _NoteIntent {
 void addNote(@Param Note note, boolean isSuccess);
 void removeNote(@Param Note note, boolean isSuccess);
}

build 生成的静态办法,比如 AddNote 办法中,只供给 “参数” 列表,不供给成果列表,成果字段皆赋予默认值,以契合目的发送场景的运用。

public static NoteIntent AddNote(Note note) {
 return new AddNote(note, false);
}

2.在 MVI-View 中发送一个 NoteIntent.AddNote(note) 目的,

public class TestFragment {
 public void onInput() {
  MVI-Model.input(NoteIntent.AddNote(note));
  }
}

3.在 MVI-Model 中处理事务逻辑,注入成果和回推目的。

由于目的为保证 “数据一致性” 而不行修改,因此在注入成果的场景下,可经过 copy 办法复制一份新的目的,而 copy 办法的入参即 “成果” 列表,以契合目的回推场景的运用。

public class NoteRequester extends MVI-Disptacher {
  protected void onHandle(NoteIntent intent){
    switch(intent.id) {
     case NoteIntent.AddNote.ID: 
       DataRepository.instance().addNote(result -> {
         NoteIntent.AddNote addNote = (NoteIntent.AddNote) intent;
         sendResult(addNote.copy(result.isSuccess));
       });
       break;
     case NoteIntent.RemoveNote.ID: 
       ...
    break;
   }
  }
}

4.在 MVI-View 中呼应目的

public class TestFragment {
 public void onOutput() {
  MVI-Model.output(this, intent-> {
     switch(intent.id) {
        case NoteIntent.AddNote.ID: 
         updateUI();
        break;
      case NoteIntent.RemoveNote.ID: 
        ...
     break;
     }
   });
  }
}

C.不带参的事情分发场景

也即没有初值传参,只用于成果分发的状况。

这种场景和 “带参数目的分发场景” 一般堆叠和互补,所以运用上其实迥然不同。

1.界说接口,办法不带 @Param 注解。那么该场景下 NoteIntent.GetNotes 静态办法供给无参和有参两种,咱们一般是运用无参,也即事情在创建时成果是被给到默认值。

@SealedClass
public interface _NoteIntent {
 void getNotes(List<Note> notes);
}

2.在 MVI-View 中发送一个 NoteIntent.GetNotes() 事情,

public class TestFragment {
 public void onInput() {
  MVI-Model.input(NoteIntent.GetNotes());
  }
}

3.在 MVI-Model 中处理事务逻辑,注入成果和回推目的。

由于目的为保证 “数据一致性” 而不行修改,因此在注入成果的场景下,可经过 copy 办法复制一份新的目的,而 copy 办法的入参即 “成果” 列表,以契合目的回推场景的运用。

public class NoteRequester extends MVI-Disptacher {
  protected void onHandle(NoteIntent intent){
    switch(intent.id) {
     case NoteIntent.GetNotes.ID: 
       DataRepository.instance().getNotes(result -> {
         NoteIntent.GetNotes getNotes = (NoteIntent.GetNotes) intent;
         sendResult(getNotes.copy(result.notes));
       });
       break;
     case NoteIntent.RemoveNote.ID: 
       ...
    break;
   }
  }
}

4.在 MVI-View 中呼应事情

public class TestFragment {
 public void onOutput() {
  MVI-Model.output(this, intent-> {
     switch(intent.id) {
        case NoteIntent.GetNotes.ID: 
         updateUI();
        break;
      case NoteIntent.RemoveNote.ID: 
        ...
     break;
     }
   });
  }
}

Github:SealedClass4Java