LoginSignup
3
0

More than 5 years have passed since last update.

javaのinterfaceでstaticメソッドに悩んでたら名前解釈順に行き着いた

Last updated at Posted at 2017-09-18

interface、staticメソッドと書いてありますが
java8で実装されたインタフェース内の静的関数の話ではないです。

こちらを期待された方はゴメンナサイ。



先日、アレ?
って思ったことがあったのでメモ的な感じでのこしておきます。

ArleneFamily.java
public final class ArleneFamily {

    public static final class Father {
        public static String name() {
            return "アイリーンのパパ";
        }
        public static String birthDay() {
            return "3/2";
        }
    }

    public static final class Mother {
        public static String name() {
            return "アイリーンのママ";
        }
        public static String birthDay() {
            return "10/30";
        }
    }
}
Sample1.java
public class Sample1 {
    public static void main(String[] args) {
        System.out.println(ArleneFamily.Father.name() + ":" + ArleneFamily.Father.birthDay());
        System.out.println(ArleneFamily.Mother.name() + ":" + ArleneFamily.Mother.birthDay());
    }
}

こんな感じの定数クラスがありまして・・・
アイリーン家構成クラス(ArleneFamily.java)と、それを表示するクラス(Sample1)になります。
実際にはアイリーン家以外にもたくさんの○○家構成クラスがある感じです。

既存仕様として各家それぞれの構成表示しかしてこなかったんですが
ブレット家もシンディ家もまとめて誕生日表示がしたい!!
というような事があり・・・

Sample2.java
public class Sample2 {
    public static void main(String[] args) {
        System.out.println(ArleneFamily.Father.name() + ":" + ArleneFamily.Father.birthDay());
        System.out.println(ArleneFamily.Mother.name() + ":" + ArleneFamily.Mother.birthDay());
        System.out.println(BretFamily.Father.name() + ":" + ArleneFamily.Father.birthDay());
        System.out.println(BretFamily.Mother.name() + ":" + ArleneFamily.Mother.birthDay());
        
        
        
    }

こんな感じになったんですが、コレハナイダロ・・・

ということでインターフェースを付けて、
使う側は下記のような感じにしたいなぁと考えてみることに。

Sample3.java
    public static void main(String[] args) {
        printBirthDay(モヤモヤ);
        printBirthDay(モヤモヤ);
        
        
        
    }

    private static void printBirthDay(モヤモヤ) {
        System.out.println(インターフェース.name() + ":" + インターフェース.birthDay());
    }



早速、Humanインターフェースを作成して、

Human.java
public interface Human {
    public String name();
    public String birthDay();
}



impl付与。

ArleneFamily.java
public final class ArleneFamily {

    public static final class Father implement Human {
        public static String name() {
            return "アイリーンのパパ";
        }
        public static String birthDay() {
            return "3/2";
        }
    }

    public static final class Mother implement Human {
        public static String name() {
            return "アイリーンのママ";
        }
        public static String birthDay() {
            return "10/30";
        }
    }
}

はい。エラーです。
メソッドの static を外せやゴルァと怒られました。

staticがoverrideできないのは静的だし分かるけど、
インターフェースの実装強要なら書けてもいいんじゃないかと思うんだけどなぁ

まぁ
I/Fを実装する際にoverrideアノテーション付けたり、総称型もクラスと一緒でextends指定だし、やっぱりstaticがoverrideできないのと根っこは一緒なのかなぁ

しゃーないのでメソッドからstaticを外します。

ArleneFamily.java
public final class ArleneFamily {

    public static final class Father implement Human {
        public String name() {
            return "アイリーンのパパ";
        }
        public String birthDay() {
            return "3/2";
        }
    }

    public static final class Mother implement Human {
        public String name() {
            return "アイリーンのママ";
        }
        public String birthDay() {
            return "10/30";
        }
    }
}

はい、今度は使用している部分でエラー。↓ここの部分

System.out.println(ArleneFamily.Father.name() + ":" +

インスタンス作成しろや!・・・ってまぁstatic外したんだからそうなるよね。

仕方ない、使用している側を直していくか・・・結構数あるし、影響範囲でかくなるなぁ
と思ったときにビビビっと閃きました!

ArleneFamily.java
public final class ArleneFamily {

    public static final Human Father = new Human() {
        @Override
        public String name() {
            return "アイリーンのパパ";
        }
        @Override
        public String birthDay() {
            return "3/2";
        }
    };

こんな感じにすれば使用側も触らなくてよくね?

やってみたら
案の定、これだと使用側もエラーにならず・・・↓ここの部分。うまく通る。

System.out.println(ArleneFamily.Father.name() + ":" +

さらにインスタンス生成という形でクラス構成が確定したので
モヤモヤ部分の書きっぷりも解消。

Sample3.java
    public static void main(String[] args) {
        printBirthDay(ArleneFamily.Father);
        printBirthDay(ArleneFamily.Mother);
        
        
        
    }

    private static void printBirthDay(Human human) {
        System.out.println(human.name() + ":" + human.birthDay());
    }

一応、もともとConstantクラスだったからnewされないように一応デフォルトコンストラクタをprivateにしておいてっと。
これで無事、当初の目的の改修を完了~~

さてやっと本題です。(笑)

上記でコンパイルが通るということは
もともとの内部クラス(Father)とHumanインターフェースを実装した変数(Father)
って共生できるのか?できたらどっちが優先されるのか?という疑問に・・・

ArleneFamily.java
public final class ArleneFamily {

    public static final Human Father = new Human() {
        @Override
        public String name() {
            return "アイリーンのパパ";
        }
        @Override
        public String birthDay() {
            return "3/2";
        }
    };

    public static final class Father {
        public static String name() {
            return "アイリーンの父親";
        }
        public static String birthDay() {
            return "11/12";
        }
    }

はい、共生できました。。
返却値を変えてどちらが実際に呼ばれるのか試してみる。

実行結果
アイリーンのパパ:3/2

変数が優先されました。
いろいろと調べてみた結果
ココの6.4.2. Obscuring、6.5. Determining the Meaning of a Name
にちゃんと書いてありました。

変数は型より優先。型はパッケージより優先。

知らなかった・・・
まぁ現場じゃわざわざ同名にするなんてありえないしね。
今回いい機会で知れました。

ちなみにFamilyを例にしましたが、実際には商品系です。
名前を置き換えたらこんな感じに orz
なんか違和感が。

javaのチラ裏の話なので、見出しなしです。

3
0
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
3
0