Java汎型extensキーワード設定境界の実現


本稿では、一般的な定義における<>の中のプレースホルダが、どのようにextendsのキーワードに合わせて使用されるかを紹介します。汎型定義は、この3つの形態に存在する。汎型、汎型インターフェース、汎型の方法。
  • の一般的な汎型定義の中の〈T〉は〈T extens Object〉に相当し、タイプ消去はタイプパラメータをTの上界、すなわちObjectに消去する。一般的な定義ではTタイプのオブジェクトとしてObjectの関数と属性を呼び出すことができます。
  • は、extensの汎型定義の「T extends Integer」を使用しています。その上界は明らかに定義されています。このとき、タイプパラメータをIntegerに消去します。汎用定義ではTタイプのオブジェクトとしてIntegerの関数と属性を呼び出すことができます。
  • 次に、いくつかの例と具体的な分析で、残りの知識点を説明します。
    タイプパラメータ多重境界の分析
    この例の汎型クラスは、タイプパラメータが複数の境界を持つ。次のような実用的な意味:Dimensionは物体の方位、HasColorは物体の色、Weightは物体の重さを表します。
    
    interface HasColor { java.awt.Color getColor(); }
    
    class Colored<T extends HasColor> {
      T item;
      Colored(T item) { this.item = item; }
      T getItem() { return item; }
      // The bound allows you to call a method:
      java.awt.Color color() { return item.getColor(); }
    }
    
    class Dimension { public int x, y, z; }
    
    // This won't work -- class must be first, then interfaces:
    // class ColoredDimension<T extends HasColor & Dimension> { }
    
    // Multiple bounds:
    class ColoredDimension<T extends Dimension & HasColor> {
      T item;
      ColoredDimension(T item) { this.item = item; }
      T getItem() { return item; }
      java.awt.Color color() { return item.getColor(); }
      int getX() { return item.x; }
      int getY() { return item.y; }
      int getZ() { return item.z; }
    }
    
    interface Weight { int weight(); }
    
    // As with inheritance, you can have only one
    // concrete class but multiple interfaces:
    class Solid<T extends Dimension & HasColor & Weight> {
      T item;
      Solid(T item) { this.item = item; }
      T getItem() { return item; }
      java.awt.Color color() { return item.getColor(); }
      int getX() { return item.x; }
      int getY() { return item.y; }
      int getZ() { return item.z; }
      int weight() { return item.weight(); }
    }
    
    class Bounded extends Dimension implements HasColor, Weight {
      public java.awt.Color getColor() { return null; }
      public int weight() { return 0; }
    }
    
    public class BasicBounds {
      public static void main(String[] args) {
        Solid<Bounded> solid = new Solid<Bounded>(new Bounded());
        solid.color();
        solid.getY();
        solid.weight();
      }
    } ///:~
  • class Colored<T extends Has Color>という汎型の定義には、タイプパラメータTの境界がHas Colorであることが要求されています。だからこそ、関数java.awt.Coolorカラー(){return item.get Color();では、Tタイプの変数itemによって、HasColorに属する方法を呼び出すことができます。
  • class ColoredDimension<T extends HasColor&Dimension>{}という境界が定義されています。この場合、境界はHas Color&Dimensionとして定義されていますが、コンパイラはプレースホルダを要求した後のextendsの最初のクラスでなければなりません。class Colored Dimension<T extends Dimension&Has Color>は正確な定義を与えています。すなわち最初はクラスでなければなりません。その後はインターフェースでなければなりません。
  • class ColoredDimension<T extends Dimension&Has Color>のクラス定義は、Tの境界がHas Color&Dimensionであるため、クラス定義ではDimensionの属性を取得することもでき、Has Colorの方法を呼び出すこともできます。
  • class Solid<T extends Dimension&Has Color&Weight>のクラス定義では、extensの後の最初のクラスはクラスで、その後は全部インターフェースで、先ほどのルールに合います。同様に、これらの境界から属性を取得し、方法を呼び出すこともできる。
  • class Bounded extends Dimension implements Has Color、Weightというクラスは汎型のオブジェクトを生成して、具体的なタイプをBoundedと指定します。class Solid<T extends Dimension&Has Color&Weight>のタイプパラメータTの要求はextends Dimension&Has Color&Weightですので、具体的なタイプをnew Solid<Bounded>に指定してもいいです。クラス定義におけるコンストラクタの声明はSolid(T item)であり、具体的なタイプはnew Solid<Bounded>に指定されたBoundedであるため、コンストラクタの実参はBoundedまたはBoundedのサブクラスであることが要求される。
  • 
    class derivedBounded extends Bounded {}
    
    class Bounded1 extends Dimension implements HasColor, Weight {
      public java.awt.Color getColor() { return null; }
      public int weight() { return 0; }
    }
    
    public class BasicBounds {
      public static void main(String[] args) {
        //Solid<Bounded> solid = new Solid<Integer>(new derivedBounded());//            
        Solid<Bounded> solid1 = new Solid<Bounded>(new derivedBounded());//        Bounded   
        //Solid<Bounded> solid2 = new Solid<Bounded>(new Bounded1());//    ,           
        solid1.color();
        solid1.getY();
        solid1.weight();
      }
    } ///:~
     前の条によれば、new Solid(new Bounded()ここで指定された具体的なタイプは、汎型クラスで定義されているTタイプのパラメータの要求extends Dimension&Has Color&Weightと一致しないので、コンパイルはエラーを報告します。コンストラクタに値を送る時、実際の参はBoundedのサブクラスとすることができます。同じ境界を継承したクラスのBounded 1は、タイプが既にBoundedに指定されているため、コンストラクタに渡されません。
    しかし、タイプパラメータが複数の境界がある場合、java内部であるjavaバイトコードはどのように処理されますか?
    
     public static void main(java.lang.String[]);
      Code:
        0: new      #2         // class Solid
        3: dup
        4: new      #3         // class Bounded
        7: dup
        8: invokespecial #4         // Method Bounded."<init>":()V
       11: invokespecial #5         // Method Solid."<init>":(LDimension;)V
       14: astore_1
    
    Method Solid.「init」から:(LDimension;)Vは、Solidのコンストラクタにパラメータを渡すとき、コンパイラはこのモジュレータがDimensionであると考えています。これはコンパイラが複数の境界を扱う方法で、第一の境界、すなわちタイプ消去が第一の境界として永遠に処理されます。しかし、残りの二つの境界はどうすればいいですか?ここは第一の境界を処理されました。またSolid.classの逆コンパイルコードを見に行きます。
    
    // Source code recreated from a .class file by IntelliJ IDEA
    // (powered by Fernflower decompiler)
    
    import java.awt.Color;
    
    class Solid<T extends Dimension & HasColor & Weight> {
      T item;
    
      Solid(T item) {
        this.item = item;
      }
    
      T getItem() {
        return this.item;
      }
    
      Color color() {
        return ((HasColor)this.item).getColor();//         ,     
      }
    
      int getX() {
        return this.item.x;
      }
    
      int getY() {
        return this.item.y;
      }
    
      int getZ() {
        return this.item.z;
      }
    
      int weight() {
        return ((Weight)this.item).weight();//         ,     
      }
    }
    呼び出す方法が第一の境界に属さない場合、タイプ変換処理を他の境界にすればいいです。とにかくTはextends Dimension&Has Color&Weightに適合しています。
    境界条件のある汎型クラスを継承する
    上記の例を観察すると、Colored、ColoredDimension、Solidの3つの種類は、保有対象の面で冗長なところがあります。同じメンバー変数、同じコンストラクタ、同じget関数があります。タイプパラメータにおいても境界は順次重畳されている。同様に、これらの境界によってもたらされる属性と方法も冗長性である。
    したがって、次の例では、それを修正しました。継承によって冗長性を除去します。注意してください。以下に継承される汎型のパラメータには境界条件があります。
    
    //HoldItem   T    
    class HoldItem<T> {
      T item;
      HoldItem(T item) { this.item = item; }
      T getItem() { return item; }
    }
    //Colored2   T HasColor   
    class Colored2<T extends HasColor> extends HoldItem<T> {
      Colored2(T item) { super(item); }
      java.awt.Color color() { return item.getColor(); }
    }
    //ColoredDimension2   T Dimension & HasColor   
    class ColoredDimension2<T extends Dimension & HasColor>
        extends Colored2<T> {
      ColoredDimension2(T item) { super(item); }
      int getX() { return item.x; }
      int getY() { return item.y; }
      int getZ() { return item.z; }
    }
    //Solid2   T Dimension & HasColor & Weight   ,         
    class Solid2<T extends Dimension & HasColor & Weight>
        extends ColoredDimension2<T> {
      Solid2(T item) { super(item); }
      int weight() { return item.weight(); }
    }
    
    public class InheritBounds {
      public static void main(String[] args) {
        Solid2<Bounded> solid2 =
            new Solid2<Bounded>(new Bounded());
        solid2.color();
        solid2.getY();
        solid2.weight();
      }
    } ///:~
  • HoldItemという汎型クラスは、メンバー変数、コンストラクタ、get関数をタイプパラメータTにより定義し、その後のクラスはそれを継承することによってこれらの属性と方法を得ることができる。
  • Colored 2汎型はHoldItemを継承し、後者の属性と方法を得て冗長性を低減した。また、class Colored 2<T extends HasColor>は「汎型を定義する」に属し、extends HoldItem<T>は「汎型を使用する」に属し、汎型類を使用するには具体的なタイプを指定する必要があり、具体的なタイプを確定するためには<T extends HasColor>の確認に遅れています。また境界が適合しているかどうかの状況から分析すると、HoldItemの要求は<T>が無境界に属し、<T_extends_Has Color>という境界定義はHas Color境界に属し、範囲的には<T_extends_Has Color>が<T>以下であることができます。TはHas Color境界を追加しているので、item.get Color()メソッドを呼び出すことができます。
  • ColoredDimension 2汎型クラスはColored 2を継承しました。境界が適合しているかどうかの状況から分析すると、Colored 2のTに対する境界条件は<T extends Has Color>であり、ColoredDimension 2の定義におけるTの境界は<T extends Dimension&Has Color>であり、<T extends Dimension&Has Color>が以下である。言い換えれば、ColoredDimenion 2定義におけるTの境界はColored 2の境界要件と一致しなければならないか、または範囲がより小さい必要がある。
  • Solid 2汎型はColoredDimension 2を継承しました。境界が合うかどうかの状況から分析して、はColoredDimension 2に等しい境界要求〈T extension&Has Color〉より小さいです。
  • まとめてみます
    つの汎型クラスが別の汎型クラスを継承する場合(前者は「定義汎型クラス」、後者は「汎型クラスを使用する」)、そして同じタイプのパラメータを使用した場合、汎型クラスを定義するタイプのパラメータ境界定義は使用するその汎型クラスの境界要件よりも小さくなければならない。
    汎型法における境界定義
    汎型法では,タイプパラメータの境界定義は,使用する汎型クラスの境界要件にも適合していなければならない。この例では、汎型類は同様に他の汎型類を継承しており、分析は上記に及ばない。次のクラスの実際的な意味:一連のインターフェースは超能力、一連のクラスはスーパーヒーローを表しています。それらは超能力のメンバー変数を持っています。
    
    import java.util.*;
    
    interface SuperPower {}
    interface XRayVision extends SuperPower {
      void seeThroughWalls();
    }
    interface SuperHearing extends SuperPower {
      void hearSubtleNoises();
    }
    interface SuperSmell extends SuperPower {
      void trackBySmell();
    }
    
    class SuperHero<POWER extends SuperPower> {
      POWER power;
      SuperHero(POWER power) { this.power = power; }
      POWER getPower() { return power; }
    }
    
    class SuperSleuth<POWER extends XRayVision> extends SuperHero<POWER> {
      SuperSleuth(POWER power) { super(power); }
      void see() { power.seeThroughWalls(); }
    }
    
    class CanineHero<POWER extends SuperHearing & SuperSmell> extends SuperHero<POWER> {
      CanineHero(POWER power) { super(power); }
      void hear() { power.hearSubtleNoises(); }
      void smell() { power.trackBySmell(); }
    }
    
    class SuperHearSmell implements SuperHearing, SuperSmell {
      public void hearSubtleNoises() {}
      public void trackBySmell() {}
    }
    
    class DogBoy extends CanineHero<SuperHearSmell> {
      DogBoy() { super(new SuperHearSmell()); }
    }
    
    public class EpicBattle {
      // Bounds in generic methods:
      static <POWER extends SuperHearing>
      void useSuperHearing(SuperHero<POWER> hero) {//    
        hero.getPower().hearSubtleNoises();
      }
      static <POWER extends SuperHearing & SuperSmell>
      void superFind(SuperHero<POWER> hero) {//    
        hero.getPower().hearSubtleNoises();
        hero.getPower().trackBySmell();
      }
      public static void main(String[] args) {
        DogBoy dogBoy = new DogBoy();
        useSuperHearing(dogBoy);
        superFind(dogBoy);
        // You can do this:
        List<? extends SuperHearing> audioBoys;
        // But you can't do this:
        // List<? extends SuperHearing & SuperSmell> dogBoys;
      }
    } ///:~
    
    
  •  主関数におけるuseSuperHearing汎型法では、そのTに対する境界は<POWER extens SuperHearing>と定義されている。一般的なSuperHero<POWER>は、モダリティにおいて使用されており、境界に対する要求は<POWER extens SuperPower>である。SuperHearingはSuperPowerを継承しているので、境界定義は境界に対する要求に適合している。
  • 主関数におけるuseSuperHearing汎型方法では、Tに対する境界を「POWER extens SuperHearing」と定義している。そのため、方法の中でSuperHearingの方法hearSubtleNoissを呼び出すことができます。
  • その他
    この文章の例は皆javaプログラミングの思想から来ています。例自体はいいですが、どうやって作者が説明するのが少ないですか?だから、詳しい分析を加えました。これらの例はよく考えてみて、よく読んでから一般的なextensキーワードに対して深い理解ができます。
    以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。