【Dart】Objectクラスの基本

6 min read読了の目安(約5800字

【Dart】Objectクラス

参考

事前知識

  • ObjectはtoString()関数を持っている

    • 全てのオブジェクトはtoString()関数を持っている
  • Objectは==演算子を持っている
    ※同じオブジェクト(hashCodeが同じオブジェクト)かを判断する

    • 全てのオブジェクトは==演算子を持っている
  • ObjectはhashCodeプロパティを持っている

    • 全てのオブジェクトはhashCodeプロパティを持っている

toString()

  • 自作クラスからオブジェクトを作成した際に、変数名をprintすると以下のようになる
    • print('$monster');print(monster);でも同様
class Monster {
  String name;
  Monster(this.name);
}

void main() {
  final monster = Monster('Slime');
  print('$monster');
}

実行結果

Instance of 'Monster'
  • 上記は自作クラスがすべてのクラスのスーパークラスであるObjectのtoString()を持っているため可能
    • Stringが求められる際に、変数名のみで呼び出すと、自動的にtoString()がcallされる
    • そのため、ヘタにtoString ()をoverrideすると以下のように更新される
class Monster {
  String name;
  Monster(this.name);
  
  
  String toString () {
    return 'TEST';
  }
}

void main() {
  final monster = Monster('Slime');
  print('$monster');
}

実行結果

TEST

==演算子

  • デフォルトでは、同じオブジェクト(hashCodeが同じオブジェクト)を指す場合にtrueが返る
class Monster {
  String name;
  Monster(this.name);
}

void main() {
  final monster1 = Monster('Slime');
  final monster2 = monster1;
  print(monster1.hashCode);
  print(monster2.hashCode);
  print(monster1 == monster2);
}

実行結果

150374362
150374362
true
  • 以下の場合は、異なるオブジェクトを指すためfalseが返る
class Monster {
  String name;
  Monster(this.name);
}

void main() {
  final monster1 = Monster('Slime');
  final monster2 = Monster('Slime');
  print(monster1.hashCode);
  print(monster2.hashCode);
  print(monster1 == monster2);
}

実行結果

68876054
549608535
false
  • 上記を同じと判断したい場合は、==演算子をoverride

    • 方法
      • identical関数を利用し、同じオブジェクト(hashCodeが同じオブジェクト)を指す場合trueを返す
      • 次にMonsterクラスの場合は、どちらもMonsterクラス、かつname値が同じならばtrueを返す
    class Monster {
      String name;
      Monster(this.name);
    
      
      bool operator ==(Object other) {
        if (identical(this, other)) {
          return true;
        }
        if (other is Monster) {
          return this.runtimeType == other.runtimeType && this.name == other.name;
        } else {
          return false;
        }
      }
    }
    
    void main() {
      final monster1 = Monster('Slime');
      final monster2 = Monster('Slime');
      print(monster1.hashCode);
      print(monster2.hashCode);
      print(monster1 == monster2);
    }
    

    実行結果

    787479825
    1029272677
    true
    
  • hashCodeもoverride

    • 通常のhashCodeは、演算子==の比較に影響を与えるオブジェクトの状態を表す単一の整数であり、オブジェクト毎にhashCodeが存在
    • int get hashCode => name.hashCode;とoverrideすると、オブジェクト自体ではなく、name変数の値毎にhashCodeを存在させることが可能
      • ※この場合も、identical関数では本来のオブジェクトが異なるかの判断をする
class Monster {
  String name;
  Monster(this.name);

  
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other is Monster) {
      return this.runtimeType == other.runtimeType && this.name == other.name;
    } else {
      return false;
    }
  }
  
  
  int get hashCode => name.hashCode;
}

void main() {
  final monster1 = Monster('Slime');
  final monster2 = Monster('Slime');
  print(monster1.hashCode);
  print(monster2.hashCode);
  print(monster1 == monster2);
}

実行結果

272189256
272189256
true
  • 上記から、単純にオブジェクトのnameが同じであれば、同一と判断すればいいだけの場合は以下に省略可能
    • hashCodeをoverrideし、monster1.hashCode == monster2.hashCodeで判断
class Monster {
  String name;
  Monster(this.name);
  
  
  int get hashCode => name.hashCode;
}

void main() {
  final monster1 = Monster('Slime');
  final monster2 = Monster('Slime');
  print(monster1.hashCode);
  print(monster2.hashCode);
  print(monster1 == monster2);
  print(monster1.hashCode == monster2.hashCode);
}

実行結果

272189256
272189256
false
true
  • 【備考】hashCodeをoverrideした場合にも、本来のオブジェクトのhashCodeを確認
    • identityHashCode()を利用
class Monster {
  String name;
  Monster(this.name);

  
  bool operator ==(Object other) {
    if (identical(this, other)) {
      return true;
    }
    if (other is Monster) {
      return this.runtimeType == other.runtimeType && this.name == other.name;
    } else {
      return false;
    }
  }

  
  int get hashCode => name.hashCode;
}

void main() {
  final monster1 = Monster('Slime');
  final monster2 = Monster('Slime');
  print('hashCode:' + monster1.hashCode.toString());
  print('hashCode:' + monster2.hashCode.toString());
  print('identityHashCode:' + identityHashCode(monster1).toString());
  print('identityHashCode:' + identityHashCode(monster2).toString());
  print(monster1 == monster2);
}

実行結果

hashCode:272189256
hashCode:272189256
identityHashCode:783211106
identityHashCode:841221886
true

【備考】Comparable

  • 目的
    • Comparable<T>のインターフェイスを利用することで、[オブジェクト1].compareTo([オブジェクト2])の比較を可能とする
  • 実装方法
    • クラス名にimplements Comparable<Monster>を付ける
    • compareTo関数をoverrideする
class Monster implements Comparable<Monster> {
  String name;
  Monster(this.name);

  
  int compareTo(Monster other) {
    if (this.name.length < other.name.length) return -1;
    if (this.name.length > other.name.length) return 1;
    return 0; // equal
  }

  
  int get hashCode => name.hashCode;
}

void main() {
  final monster1 = Monster('Slime');
  final monster2 = Monster('XXXXXX');
  final monster3 = Monster('XXXX');
  final monster4 = Monster('XXXXX');
  print(monster1.compareTo(monster2));
  print(monster1.compareTo(monster3));
  print(monster1.compareTo(monster4));
}

実行結果

-1
1
0