0

Shape というスーパークラスがあります。

abstract class Shape {
    private String color;
    public Shape(String color) {
        this.color=color;
    }
    public String getColor(){return color;}
    abstract double Area();
}

および 2 つの継承クラス:

class Rectangle extends Shape {
   private double b,h;
   public Rectangle(double b,double h, String color) {
        super(color);
        this.b=b;
        this.h=h;         
   }
   public double getB() { return b;}
   public double getH() { return h;}
   public double Area() {
        return b*h;
   }

}

class Circle extends Shape {
   private double r;
   public Circle(double r,String color) {
        super(color);
        this.r=r;         
   }
   public double getR() {
      return r;
   }
   public double Area() {
        return 3.14*r*r;
   }
}

これで、オブジェクトまたはシェイプ (長方形と円) の配列が作成されました。私が抱えている問題は、この配列の要素を反復処理してそれらの属性を出力したいときです。私はこのようなことをしたい:

for (int i=0;i<lengthShapes;i++) {
    System.out.println(shapes[i].getB()+shapes[i].getH()+shapes[i].getR());
}

i 番目の位置にあるオブジェクトが、その属性を印刷するための Rectangle または Circle であることを認識する方法を意味します。Shape の配列しかないことに注意してください。インターフェースでできると思いますが、抽象クラスだけでどうやってやるのか。それは可能ですか?ありがとう

4

3 に答える 3

2

i 番目の位置にあるオブジェクトが Rectangle または Circle であることを認識する方法を意味します...

これを行う最も簡単な方法は、instanceof演算子を使用することです。

例えば

if(shapes[i] instanceof Rectangle) {
    Rectangle rect = (Rectangle) shapes[i];
    // ...
}

これは良い習慣とは見なされません (これらはすべて独自のクラスですが、(実行時に) オブジェクトの型を確認し、明示的なキャストを使用する必要があります)。

ただし、属性を出力するだけの場合は、両方のサブクラスに適切にオーバーライドさせてから、次のtoString()ことができます-

System.out.println(shapes[i]);
于 2013-06-02T04:03:13.157 に答える
0

一般に、異種コレクション (この場合は と の両方を含む) のアイテムを反復処理する場合、RectangleCircleの 2 つの選択肢があります。

  1. 親型 (ここでは ) で使用可能なメソッドのみを扱いますShape
  2. instanceofチェックを使用して、各サブタイプを異なる方法で処理します。

設計 A: すべての機能をShape

このデザインでは、すべてShapeの人が独自の属性を印刷する方法を知る責任があります。これは選択肢 1 を使用します。ループするコードは、aRectangleまたは aのどちらを持っているかを知る必要はありませんCircle

Shape、追加します

    abstract String getAttributesString();

Rectangleこれを次のように実装します

    @Override
    String getAttributesString() {
        return String.format("Rectangle {b=%f, h=%f}", b, h);
    }

次に、ループは

for (Shape shape : shapes) {
    System.out.println(shape.getAttributesString());
}

toString()メソッド onをオーバーライドすることもできますが、通常は、たとえばユーザーに表示する必要があるものではなく、デバッグObjectにのみ使用することをお勧めします。toStringクラスには通常toString、インスタンスのデータの完全な表現を出力するオーバーライドが必要です。

設計 B: インスタンスのチェック

これは選択肢 2 です。変更はループ コードのみです。形状を変更する必要はまったくありません。

    for (Shape shape : shapes) {
        if (shape instanceof Rectangle) {
            Rectangle rectangle = (Rectangle) shape;
            System.out.println(String.format("Rectangle {b=%f, h=%f}", rectangle.getB(), rectangle.getH()));
        } else if (shape instanceof Circle) {
            Circle circle = (Circle) shape;
            System.out.println(String.format("Circle {r=%f}", circle.getR()));
        }
    }

デザインC:来客パターン

これは、2 つの選択肢の組み合わせのようなものです。

interface ShapeVisitor {
    void visitRectangle(Rectangle rectangle);
    void visitCircle(Circle circle);
}

abstract class Shape {

    void visit(ShapeVisitor visitor);

    /* ... the rest of your code ... */
}

class Rectangle extends Shape {

    @Override
    void visit(ShapeVisitor visitor) {
        visitor.visitRectangle(this);
    }

    /* ... the rest of your code ... */
}

class Circle extends Shape {

    @Override
    void visit(ShapeVisitor visitor) {
        visitor.visitCircle(this);
    }

    /* ... the rest of your code ... */
}

次に、ループは次のようになります。

for (Shape shape : shapes) {
    shape.visit(new ShapeVisitor() {
        @Override
        void visitRectangle(Rectangle rectangle) {
            System.out.println(String.format("Rectangle {b=%f, h=%f}", rectangle.getB(), rectangle.getH()));
        }
        @Override
        void visitCircle(Circle circle) {
            System.out.println(String.format("Circle {r=%f}", circle.getR()));
        }
    });
}

どちらを使用しますか?

設計 A はキャスティングを回避できるため優れてinstanceofいましたが、欠点は、印刷ロジックを形状クラス自体に配置する必要があり、柔軟性が失われることでした (2 つの異なる状況で同じ形状のリストを異なる方法で出力したい場合はどうなるでしょうか)。 ?)。

設計 B は印刷ロジックを必要な場所に配置しますが、キャストを使用すると、コンパイラの型チェックを十分に活用できません。Shapeたとえば、別のサブタイプを追加する場合、ループ コードの更新を忘れる可能性があるため、エラーが発生しやすくなります。

設計 C は、A と B の最良の機能を組み合わせたようなものです。ただし、問題は、拡張できないことです。他の人が独自のサブタイプを定義するために使用するライブラリを作成している場合、インターフェイスShapeを変更する必要があるため、他の人はそれを行うことができません。ShapeVisitorただし、コードがそのように拡張可能である必要がない場合に適しています。

または...

最終的に、これはすべて Scala の方がはるかに簡単です。(はい、私はもうあなたの質問に答えているわけではなく、ただ楽しんでいるだけです。)

sealed trait Shape {
  def color: String
  def area: Double
}

case class Rectangle(b: Double, h: Double, color: String) extends Shape {
  def area: Double = b*h
}

case class Circle(r: Double, color: String) extends Shape {
  def area: Double = math.Pi*r*r
}

for (shape <- shapes) {
  println(shape match {
    case rectangle: Rectangle =>
      "Rectangle {b=%f, h=%f}".format(rectangle.b, rectangle.h)
    case circle: Circle =>
      "Circle {r=%f}".format(circle.r)
  })
}
于 2013-06-03T04:26:08.840 に答える
0

class からクラス識別子情報を取得できますClass。たとえば、現在のクラスの名前を として取得するにはString、次のメソッドを使用できますgetCanonicalName

System.out.println(shapes[i].getClass().getCanonicalName());

対応する値にアクセスするためのキーとして属性名を持つ HashMap を返すメソッドをShapeクラスで宣言することを検討しましたか? getAttributes円には「半径」キーがあり、長方形には「ベース」と「高さ」キーがあり、両方とも「面積」と「色」キーがあります。

于 2013-06-02T19:34:11.873 に答える