Item 52マルチ定義は慎重に使用してください

14175 ワード

コード52-1コレクション分類器-エラー!このプログラムは何を出力しますか?
public class CollectionClassifier {
    public static String classify(Set<?> s) {
        return "집합";
    }
    
    public static String classify(List<?> lst) {
        return "리스트";
    }

    public static String classify(Collection<?> c) {
        return "그 외";
    }

    public static void main(String[] args) {
        Collection<?>[] collections = {
                new HashSet<String>(),
                new ArrayList<BigInteger>(),
                new HashMap<String, String>().values()
        };

        for (Collection<?> c : collections) {
            System.out.println(classify(c));
        }
    }
}
[コレクション](Assembly)、[リスト](List)、[その他](Other)の順に出力される場合がありますが、実際に実行すると、[その他](Other)だけが3回連続して出力されます.
理由は.コンパイル時に3つのマルチ定義分類のどちらを呼び出すかを決定するため
コンパイル時、for文のcは常にCollectionタイプです.実行するたびに変化しますが、呼び出すメソッドの選択には影響しません.
したがって、コンパイル時のパラメータタイプに応じて、分類(Collection)の3つ目のメソッドのみが呼び出されます.
直感から外れた理由は?
→再定義の方法は動的に選択され、多重定義の方法は静的に選択されるからである.
メソッドを再定義し、サブクラスのインスタンスで呼び出すと、再定義メソッドが実行されます.
コンパイル時にインスタンスのタイプが何であるかにかかわらず.
コード52-2再定義メソッド呼び出しメカニズム-このプログラムは何を出力しますか?
class Wine {
    String name() { return "포도주"; }
}

class SparklingWine extends Wine {
    @Override
    String name() {
        return "발포성 포도주";
    }
}

class Champagne extends SparklingWine {
    @Override
    String name() {
        return "샴페인";
    }
}


public class Overriding {
    public static void main(String[] args) {
        List<Wine> wineList = List.of(
                new Wine(), new SparklingWine(), new Champagne());

        for (Wine wine : wineList) {
            System.out.println(wine.name());
        }
    }
}
Wineクラスで定義されたnameメソッドは、サブクラスSparklingWineおよびChampaineで再定義されます.
予想通り、このプログラムは「ワイン」、「発泡性ワイン」、「シャンパン」の順に出力される.
コンパイル時のタイプがWineであっても、常に「最下層から再定義」の再定義方法が実行されます.
一方、マルチ定義メソッド間では、オブジェクトのランタイムタイプは重要ではありません.
コンパイル時に、パラメータにのみ依存するコンパイル時間タイプを選択します.
コード52−1の問題は、CollectionClassifierのすべての分類方法を統合することである.
instanceofで明示的な検査を行うことで、問題を徹底的に解決することができます.
public static String classify(Collection<?> c) {
    return c instanceOf Set ? "집합" : 
           c instanceOf List? "리스트": "그 외"; 
プログラマーにとって、再定義は正常な動作であり、多定義は例外的な動作である.
すなわち、再定義された方法はプログラマの希望に従って動作するが、CollectionClassifierの例に示すように、複数の定義はこれらの期待を軽視する.
紛らわしいコードは書かないほうがいいです.特にAPIを公開するには、もっと注意しなければならない.
APIユーザがパラメータを渡すときにどのようなマルチ定義メソッドを呼び出すか分からない場合、プログラムはエラーを起こしやすい.
実行時に奇妙に表現され、APIユーザーは問題の診断に時間がかかります.
だから多重正義が混同することを避けなければならない.
*パラメータ数が同じ複数の定義は、セキュリティと保守のために作成しないでください.
可変パラメータ(varargs)を使用する方法の場合、複数の定義は行わないでください.
多重定義よりも、方法に異なる名前をつける道が開かれている.
ObjectOutputStreamクラスの表示
このクラスのwriteメソッドには、すべての基本タイプといくつかの参照タイプの変形があります.
しかし、多重定義ではなく、すべての方法に異なる名前を付けることを選択しました.
writeBoolean、writeInt、writeLongに似ています.
この方法が多重定義よりも優れているもう一つの点は、readメソッドの名前とペアを組むのに適していることです.
たとえば、readBoolean()、readInt()およびreadLong()です.
プリアンブル52−3のパラメータは、2つの方法が同じ動作を実行することを保証する.
public boolean contentEquals(StringBuffer sb) {
    return contentEquals((CharSequence) sb);
}
JAvaライブラリは今回のプロジェクトの精神を維持しようと努力していますが、いくつかの失敗したクラスもあります.
たとえば、Stringクラスの値Of(char[])と値Of(Object)は、同じオブジェクトが渡されてもまったく異なる操作を実行します.
このような理由はありませんが、混乱を引き起こす可能性のあるエラーケースです.

コアの整理

  • プログラミング言語で複数の定義を許可することは、必ずしも複数の定義を使用することを意味しない.
  • は、一般に、パラメータの数が同じである場合、多重定義を避けることが望ましい.
  • 、特に作成者であれば、これらの提案に従うことはできません.
  • では、混同される可能性のあるパラメータを変換して、正しいマルチ定義メソッドを選択する必要があります.
  • これが不可能である場合、例えば、新しいインタフェースを実装するために既存のクラスを変更する必要がある場合、同じオブジェクトを入力するマルチ定義メソッドを同じ動作にする必要があります.
  • それ以外の場合、プログラマはマルチ定義メソッドまたはジェネレータを効率的に使用することができず、なぜ予想通りに操作できないのか理解できません.