ふみぽん's diary

技術的な備忘録が主のブログ

Java実践 インスタンスの基本操作 

f:id:fumipow2317:20190608104719p:plain

インスタンスの基本操作についての備忘録


1. インスタンス5大基本操作

1-1.オブジェクトクラスの基本機能

Javaにおいて全てのクラスは、Java.lang.Obkectクラスを継承している。

Objectクラスによるメリット

  1. 全てのクラスは、Objectクラスで定義されているメソッドを持っていることが保証される。

  2. Object型の変数には、あらゆるインスタンスを代入できる。

全てのインスタンスが使えるメソッド一覧表

メソッド 説明
toString() 文字列表現を行う
equals() 等価判定を行う
hasCode() ハッシュ値を得る
compareTo() 大小関係を判定する
clone() 複製する

2. toString()インスタンスの文字列表現

2-1.toString()メソッドについて

インスタンスの内容を読んで理解できるように文字列で表したもの(文字列表現)にするメソッド。
例えば、下記のHumanクラスのインスタンスhを生成し、System.out.println(h);を実行すると、
実行結果:Human@15db9742となる。

  1. System.out.println(h)の内部では、h.toString()が実行されて文字列表現を取得
  2. 取得した文字列表現をコンソールに出力している

⇨結果的に人が見ても意味がわからない表示になってしまっている

public class Human {

    private String name;

    public Human(String name)
    {
        this.name = name;
    }

}

2-2.toString()をオーバライドする

2-1.で記述した人が見てもわからないインスタンスの文字列表現を解決するには、
java.util.Object#toString()をHumanクラスにとって適切な適切な文字列表現になるように
オーバーライドすれば良い。

public class Human {

    private String name;

    public Human(String name)
    {
        this.name = name;
    }

    //java.util.Object#toString()をOverride
    public String toString(){
        return "名前は" + this.name + "です";
    }

}

<まとめ>

  • 新たなクラスを開発したらtoString()をオーバライドしておくことで意図した文字列表現を出力することが可能になる。

3. equals()インスタンスの等価判定

3-1.等価と等値の違い

等価とは ( equals() )

  • 指している2つのものが「同じ内容」であること。
  • 同じアドレスを指していなくてもOK。

等値とは ( ==演算子 )

  • 指しているものが「完全に同一の場所に存在」すること。
  • 同じアドレス(参照先)を指していること。

3-2.equals()をオーバーライドする

java.util.Object#equals()で等価判定を行う際に内部は、ただの等値判定でしかない。
それはクラスの開発者に「何をもって等価とするのか」ということを委ねているからである。

   //equals()のオーバライド定石方法
    public boolean equals(Object o)
    {
        //①自分自身が引数として渡された場合はtrue
        if(o == this)
        {
            return true;
        }
        //②nullが引数として渡された場合はfalse
        if(o == null)
        {
            return false;
        }
        //③比較して型が異なる場合はfalse
        //[ 変数 instanceof クラス名 ]で***クラスのインスタンスかどうか判定できる
        if(!(o instanceof Human))
        {
            return false;
        }
        //型が同じであれば比較できるように適切にキャストする
        Human h = (Human) o;
        //例として先頭と末尾の空白を削除してnameが同じであれば、等価とみなす
        if(!this.name.trim().equals(h.name.trim()))
        {
            return false;
        }
        return true;
    }

<まとめ>

  • 新たなクラスを開発したら、必ずequals()をオーバライドすること。
  • オーバライドが不要なクラス
    • 等価の判定をされることが考えられないクラス。(ビジネスロジックを担当するクラス)
    • 親クラスですでにオーバライドされていて、そのロジックを使用して問題ないクラス。

4. hashCode()インスタンスの要約について

4-1.コレクションクラスHashSetの内部構造

概要
ArrayListremove()を使って特定のインスタンスを削除しようといた場合、
インスタンスの順番にequals()で削除する対象のインスタンスかどうか判定をしているが
時間がかかり効率がとても悪い。

そこで、HashSetやHashMapのようなHash系のコレクションクラスでは、

  1. 高速だが、曖昧な方法で、各要素に「だいたい同じか?」を問い合わせる
  2. 「だいたい同じ」な要素にだけ、equals()で「厳密に同じか?」を問い合わせる

まず「だいたい同じか?」の判定方法

ハッシュ値とは:インスタンスの内容を数値(int型の整数)として要約したもの。

//ハッシュ値の条件
・ 同じ(等価:同じ参照でなくてOK)インスタンスからは、必ず同じハッシュ値が取得できること。
・異なるインスタンスからは、なるべく異なるハッシュ値が取得できること。
 (異なるインスタンスでもごく稀に同じハッシュ値になってしまうこともある)

4-2.hashCode()をオーバーライドする

全てのインスタンスにはhashCode()を呼び出したさいにハッシュ値の条件に従った値を返す義務がある。

///hashCode()のオーバーライド
public int hashCode(){
  
  //適当な初期値を決める(0以外であればなんでもいい)
  int result = 23;
  //各フィールドの影響を加える(各要素に影響を与える乗数には31がよく用いられる
  result = result * 31 + name.hashCode();
  return result;
}

<まとめ>

  • 新たなクラスを開発したら、必ずhashCode()をオーバライドすること。

5. compareTo()インスタンスの順序づけ

5-1.インスタンスの並び替え

  • Collectionクラスのsort()で中身の要素を順番に並び替えてくれる。
  • 単に「並び替えろ」とJVMに指示するだけではエラーとなってしまうので何順か定めてあげる必要あり。
  • 一般的に想定される並べ順を「自然順序づけ」をいう。

5-2.Comparableインターフェースの実装

  • あるクラスの自然順序を宣言するためには、java.lang.Comparableインターフェースを実装する。
  • このインターフェースを実装すると、compareTo()メソッドのオーバーライドが強制される。
  • compareTo()メソッドを使って自然順序づけの方法を宣言することができる。

compareTo(Object obj)メソッド: 引数で渡されたインスタンスを自分自身とを比較して、大小関係を判定する。

返却する値 条件
負の数 引数インスタンスのが大きい
0(ぜろ) 自分自身と引数インスタンスが同じ
正の数 引数のインスタンスのが大きい

Humanクラスが以下を満たすクラスであれば、Collectionsクラスのsort()は、 格納したいるインスタンスcompareTo()を呼び出して大小関係を比較しながら並び替えを実行してくれる。

  • compareTo()を持つ
  • java.lang.Comparableインターフェースを実装して「自然順序づけ」を定義している
//Comparableインターフェースを実装 (<***>は自身で決定する)
public class Human implements Comparable<Human>{

    private String name;
    private int age;

    //(この間も何か処理が記載されている)

    @Override
    public int compareTo(Human h) {
        
        if(this.age < h.age)
        {
            return -1;
        }
        if(this.age > h.age)
        {
            return 1;
        }
        return 0;
    }
}

<まとめ>

  • 開発したクラスにComparableインターフェースを実装し、compareTo()をオーバライドすることで自然順正づけで並び替えを行えるようになる。