0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[Dart]複数の型である可能性がある型の表現

Last updated at Posted at 2024-07-17

これらの型は、返す可能性がある型が複数で、それらに継承関係のない型がある場合に、型をdynamicやObjectなどにして縛りを不必要に開放的にせずにその関係を示す型。
型引数の型以外の型のインスタンスを持つ可能性はなく、
複数のインスタンスを所持することは起こり得ない。
引数にdynamicを使うコンストラクタで型引数の型以外の型のインスタンスが渡された時、例外を投げる;
フィールドを変更できるクラスと、定数コンストラクタが用意されているクラスがある

二つの型である可能性を持つ型
or_type.dart
//T,U のいずれか一つのobjを参照している型;第一引数がT;両方が同じ型ならその型を代わりに使うべき;nullを含むものは構築できない;両方がnullの時はtsではなくtSなどでないと値を与えれないことに注意
class OrT<T extends Object,U extends Object>implements OrTc{
  OrT.Ft(T t):_t=t;OrT.Fu(U u):_u=u;
  ///TかUの許される方の型の変数に代入;T型が優先される。
  OrT(o){if(o is T){_t=o;return;}if(o is U){_u=o;return;}throw OrTExcep.unsupportedTypeConstruction(o.runtimeType);}
  OrT._();//子クラスがデフォルトコンストラクタを解決する為のプライベートデフォルトコンストラクタ
  T? get t{return _t;}U? get u{return _u;}
  ///有効な方の値を取得する;2型;
  dynamic get v{return _u==null?_t!:_u!;}
  ///_tがnullでないなら_tに代入;セッターじゃなくしたことで型安全に。
  void ts(T t){if(_t!=null)_t=t;}
    // set t(T? t){if(_t!=null&&t!=null)_t=t;}//tsで代用可能;tがnull許容なのは、ゲッターがnull許容である必要があるから
  ///_tに代入;_tがnullだったのなら_uをnullにする
  void tS(T t){if(_t==null)_u=null;_t=t;}
  void us(U u){if(_u!=null)_u=u;}
  void uS(U u){if(_u==null)_t=null;_u=u;}
  T? _t;U? _u;
  get runtimeTypeT{return T;}
  get runtimeTypeU{return U;}
  OrT<T,U> toOrT(){return this;}
  @override String toString(){return v.toString();}
  //ListをList<OrT>にキャスト;
  static List<OrT<T,U>> createOrTList<T extends Object,U extends Object>([List<dynamic> l=const[]]){
    var ortl=<OrT<T,U>>[];var i=0;
    while(i<l.length){
      if(l[i]is T){ortl.add(OrT<T,U>.Ft(l[i]));}
      else if(l[i]is U){ortl.add(OrT<T,U>.Fu(l[i]));}
      else{throw OrTExcep.unsupportedTypeConstruction(l[i].runtimeType);}++i;}
    return ortl;}
}
定数コンストラクタ対応版
or_type.dart
//OrTの定数コンストラクタ用意版;フィールド変更手段が無い。
class OrTc<T extends Object,U extends Object>{
  const OrTc.Ft(T t):t=t,u=null;const OrTc.Fu(U u):u=u,t=null;
  const OrTc._():t=null,u=null;//子クラスがデフォルトコンストラクタを解決する為のプライベートデフォルトコンストラクタ
  dynamic get v{return u==null?t!:u!;}
  final T? t;final U? u;
  get runtimeTypeT{return T;}
  get runtimeTypeU{return U;}
  OrT<T,U> toOrT(){return u==null?OrT.Ft(t!):OrT.Fu(u!);}
  @override String toString(){return v.toString();}
  static List<OrTc<T,U>> createOrTcList<T extends Object,U extends Object>([List<dynamic> l=const[]]){
    var ortl=<OrTc<T,U>>[];var i=0;
    while(i<l.length){
      if(l[i]is T){ortl.add(OrTc<T,U>.Ft(l[i]));}
      else if(l[i]is U){ortl.add(OrTc<T,U>.Fu(l[i]));}
      else{throw OrTExcep.unsupportedTypeConstruction(l[i].runtimeType);}++i;}
    return ortl;}
}
簡単な利用例
or_type.dart
//dynamicやObjectなどを帰り値型にすることも可能だが、intとString以外の型が返ることはないので縛った方が適切だと考える。
///OrTの実用例;偶数ならStringでそれを知らせる、それ以外ならその数をそのまま返す。
OrTc<int,String> gusu(int i){
  return i%2==0?OrTc.Fu("偶数です。"):OrTc.Ft(i);
}
三つの型である可能性を持つ型
or_type.dart
//T,U,B のいずれかのobjを参照している型;
class OrrT<T extends Object,U extends Object,B extends Object>extends OrT<T,U>{
  // print(_t);print(_u);print(_b);
  @override OrrT._():super._();@override OrrT.Ft(T s):super(s);@override OrrT.Fu(U s):super(s);
  OrrT.Fb(B s):super._(){_b=s;}
  @override OrrT(o):super._(){if(o is T){_t=o;return;}if(o is U){_u=o;return;}if(o is B){_b=o;return;}
    throw OrTExcep.unsupportedTypeConstruction(o.runtimeType);}
  ///有効な方の値を取得する;両方がnullであることはあり得ないので安定
  @override dynamic get v{
    return _t!=null?_t!:
      _u!=null?_u!:
        _b!;}
  void ub(B s){if(_b!=null)_b=s;}void bS(B s){if(_b==null)_t=_u=null;_b=s;}//tをnullにして ;
  B? get b{return _b;}B? _b;
  get runtimeTypeB{return B;}
  @override String toString(){return v.toString();}
  OrT? get toOrT{if(_b!=null)return this as OrT<T,U>;return null;}
  static List<OrrT<T,U,B>> createOrTList<T extends Object,U extends Object,B extends Object>([List<dynamic> l=const[]]){
    var ortl=<OrrT<T,U,B>>[];var i=0;
    while(i<l.length){
      if(l[i]is T){ortl.add(OrrT<T,U,B>.Ft(l[i]));}
      else if(l[i]is U){ortl.add(OrrT<T,U,B>.Fu(l[i]));}
      else if(l[i]is B){ortl.add(OrrT<T,U,B>.Fb(l[i]));}
      else{throw OrTExcep.unsupportedTypeConstruction(l[i].runtimeType);}++i;}
    return ortl;}
}
定数コンストラクタ対応版
or_type.dart
class OrrTc<T extends Object,U extends Object,B extends Object>extends OrTc<T,U>{
  const OrrTc.Ft(T t):b=null,super.Ft(t);const OrrTc.Fu(U u):b=null,super.Fu(u);const OrrTc.Fb(B b):b=b,super._();
  ///TかUの許される方の型の変数に代入;T型が優先される。
  const OrrTc._():b=null,super._();
  dynamic get v{return u==null?t!:u!;}
  final B? b;
  get runtimeTypeT{return T;}
  get runtimeTypeU{return U;}
  @override String toString(){return v.toString();}
  static List<OrrTc<T,U,B>> createOrrTcList<T extends Object,U extends Object,B extends Object>([List<dynamic> l=const[]]){
    var ortl=<OrrTc<T,U,B>>[];var i=0;
    while(i<l.length){
      if(l[i]is T){ortl.add(OrrTc<T,U,B>.Ft(l[i]));}
      else if(l[i]is U){ortl.add(OrrTc<T,U,B>.Fu(l[i]));}
      else{throw OrTExcep.unsupportedTypeConstruction(l[i].runtimeType);}++i;}
    return ortl;}
}
二つの型である可能性を持つ型の、targetで管理する版。これではnullableも利用可能
or_type.dart
//T,U のいずれか一つのobjを参照している型;第一引数がT;両方が同じ型ならその型を代わりに使うべき;nullを許容する
class OrTNull<T,U>{
  late int target;
  OrTNull.Ft(T t):target=0{_t=t;}OrTNull.Fu(U u):target=1{_u=u;}
  ///TかUの許される方の型の変数に代入;T型が優先される。
  OrTNull(o){if(o is T){_t=o;target=0;return;}if(o is U){_u=o;target=0;return;}throw OrTExcep.unsupportedTypeConstruction(o.runtimeType);}
  OrTNull._();
  T? get t{return _t;}U? get u{return _u;}
  ///有効な方の値を取得する;null型ならnullを返す
  dynamic get v{return target==0?T==Null?null:_t!:U==Null?null:_u!;}
  ///_tがnullでないなら_tに代入;tがnull許容なのは、ゲッターがnull許容である必要があるから
  void ts(T t){if(target==0)_t=t;}
  ///_tに代入;_tがnullだったのなら_uをnullにする
  void tS(T t){if(target==1){_u=null;target=0;}_t=t;}
  void us(U u){if(target==1)_u=u;}
  void uS(U u){if(target==0){_t=null;target=1;};_u=u;}
  T? _t;U? _u;
  @override String toString(){return v.toString();}
  static List<OrTNull<T,U>>createOrTList<T,U>([List<dynamic> l=const[]]){
    var ortl=<OrTNull<T,U>>[];var i=0;
    while(i<l.length){
      if(l[i]is T){ortl.add(OrTNull<T,U>.Ft(l[i]));}
      else if(l[i]is U){ortl.add(OrTNull<T,U>.Fu(l[i]));}
      else{throw OrTExcep.unsupportedTypeConstruction(l[i].runtimeType);}++i;}
    return ortl;}
}
例外
or_type.dart
class OrTExcep implements Exception {
  final String message;
  OrTExcep.unsupportedTypeConstruction(Type t):message="unsupportedTypeConstruction>>$t;対応していない$t型で構築が試みられた";
  @override
  String toString() {
    return "Bad State: $message";
  }
}
色々なものの利用例
or_type.dart
void main(){
  print(gusu(1));//1
  print(gusu(2));//偶数です
  
  List<OrT<int,bool>> ortl=OrT.createOrTList<int,bool>([1,2,true,3,false]);
  print(ortl);
  var ort=OrT<int,bool>.Fu(true);
  print(ort);//true
  ort.tS(9);
  print(ort);//9
  ort.ts(2);
  print(ort.t!+2);//4
  print("");
  var onull=OrTNull<Null,int>(null);
  print(onull);print(onull.target);
  onull.ts(null);
  print(onull);print(onull.target);
  onull.uS(9);
  print(onull);print(onull.target); 
  // OrT<bool,int>("a");//例外

  var orrt=OrrT<bool,int,double>(0);
  print(orrt);
}

継承関係にすべきだと考えたが、無駄に重くするのは避けたかったので一部に「ClasS._(){}」を利用した。多分非常識な書き方なのだろうが、ほかに適した書き方がわかりませんでした。知っている方は特に、コメントをくださると嬉しいです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?