Подтвердить что ты не робот

Является ли это непреложным классом?

Я понятия не имею, какой должен выглядеть неизменный класс, но я уверен, что это так. Я прав? Если я не укажу, что нужно добавить/удалить.

import java.io.Serializable;

public class Triangle implements IShape, Serializable {
    private static final long serialVersionUID = 0x100;

    private Point[] points;

    public Triangle(Point a, Point b, Point c) {
        this.points = new Point[]{a, b, c};
    }

    @Override
    public Point[] getPoints() {
        return this.points;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (this == obj) return true;
        if (getClass() != obj.getClass()) return false;
        Point[] trianglePoints = ((Triangle) obj).getPoints();
        for (int i = 0; i < points.length; i++){
            if (!points[i].equals(trianglePoints[i])) return false;
        }
        return true;
    }
}

Будет ли это делать трюк?

@Override
    public Point[] getPoints() {
        Point[] copyPoint = {
                new Point(points[0]),
                new Point(points[1]),
                new Point(points[2]),};
        return copyPoint;
    }

Класс точки:

import java.io.Serializable;

public class Point implements Serializable {
    private static final long serialVersionUID = 0x100;

    public int x;
    public int y;
    public int z;

    public Point(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Point(Point that) {
        this.x = that.x;
        this.y = that.y;
        this.z = that.z;
    }

    public boolean equals(Object obj) { 
        // assume this is a typical, safe .equals implementation
        // that compares the coordinates in this instance to the
        // other instance
        return true;
    }
}
4b9b3361

Ответ 1

Нет, вы можете изменить то, что в массиве Points. Если вы хотите сделать его неизменным, попросите геттера передать копию массива Points, а не оригинал.

попробуйте следующее:

Triangle triangle = new Triangle(a, b, c);
triangle.getPoints()[1] = null;
System.out.println(Arrays.toString(triangle.getPoints()));

Точка должна быть неизменной (как указывает Никита Рыбак). Как копировать массивы см. как скопировать массив в Java.

Ответ 2

Нет, нет. Вы показываете Point [], и вызывающий может изменить его содержимое. Кроме того, ваш класс не является окончательным, поэтому кто-то может подорвать его, подклассифицируя его.

Ответ 3

Нет, он определенно изменен.

Вы не только показываете фактический массив Point [], вы не защищаете-копируете (Bloch 2nd ed., Item 39) Объект Point сам берет их через конструктор.

  • В массиве Point [] могут быть элементы удалены или добавлены к нему, так что это изменяемые.
  • Вы можете переходить в пункты a,   b и c, затем вызовите setX() или setY()   на них изменить свои данные после   строительство.

Ответ 4

Закрыть. Во-первых, неизменяемый класс должен сделать его окончательным, но это не является обязательным требованием.

Однако вы выставляете массив через getter, и это не является неизменным. Создайте защитную копию, используя Arrays.copyOf(массив, длина):

@Override
public Point[] getPoints() {
    return Arrays.copyOf(this.points,this.points.length);
}

Ответ 5

Вот что я сделал бы, чтобы сделать этот класс неизменным, с помощью Guava. Я вижу из @Override в опубликованном вами коде, что IShape, кажется, требует Point[] из метода getPoints(), но я игнорирую это ради примера, поскольку использование массивов объектов - это скорее плохая идея, особенно если вы хотите неизменности (поскольку они не могут быть неизменными и все).

public final class Triangle implements IShape, Serializable {
  private final ImmutableList<Point> points;

  public Triangle(Point a, Point b, Point c) {
    this.points = ImmutableList.of(a, b, c);
  }

  public ImmutableList<Point> getPoints() {
    return this.points;
  }

  // ...
}

Point также должен быть больше похож:

public final class Point implements Serializable {
  /*
   * Could use public final here really, but I prefer
   * consistent use of methods.
   */
  private final int x;
  private final int y;
  private final int z;

  public Point(int x, int y, int z) {
    this.x = x;
    this.y = y;
    this.z = z;
  }

  // getters, etc.
}

Ответ 6

Чтобы быть неизменным классом, недостаточно, чтобы ваши методы обещают не изменять объект. Помимо того, что все поля являются частными, а методы не позволяют изменять, вы также должны гарантировать, что подклассы имеют одинаковое обещание неизменности. Это включает в себя создание самого финального класса и обеспечение того, чтобы ссылки на поля не возвращались.

Короткий, но отличный подход к этому можно найти в этой статье:

http://www.javaranch.com/journal/2003/04/immutable.htm

Ответ 7

Вам нужно не только предоставить неизменяемую копию интернализованного массива, но также нужно убедиться, что объект Point является неизменным.

Рассмотрим следующее использование класса Point в стандартном Java API:

Point a = new Point(1,1);
Point b = new Point(1,1);
Point c = new Point(1,1);
Triangle triangle = new Triangle(a, b, c);
System.out.println(Arrays.toString(triangle.getPoints()));
c.setLocation(99,99);
System.out.println(Arrays.toString(triangle.getPoints()));

Ответ 8

Это не является неизменным, потому что...

Triangle t1 = new Triangle(new Point(0,0), new Point(0, 10), new Point(10, 10));
Triangle t2 = t1;

System.out.println( t1.getPoints()[0] );  // -> 0

t2.getPoints()[0].x = 10;

System.out.println( t1.getPoints()[0] );  // -> 10

Таким образом, класс не является неизменным, поскольку вы можете изменить состояние экземпляра (внутренний Point[] открыт), и это также изменяет состояние ссылки на тот же экземпляр.

Чтобы сделать его истинным неизменяемым классом, вам понадобятся методы, позволяющие отдельно получать X и Y из каждой точки, например:

public int getPointX(int point) { return points[point].x; }
public int getPointY(int point) { return points[point].y; }

или

public Point getPoint(int point) { return new Point(points[point]); }

или верните копию points, как вы предложили в своем редактировании.

Ответ 9

В дополнение к тому, что уже отмечалось другими, вам необходимо:

  • Создайте свой треугольный класс final, чтобы предотвратить создание измененных треугольников по подклассам.
  • Объявить все поля final, чтобы случайно вызвать модификацию полей самим классом.

В "Эффективной Java" Джошуа Блох содержит список правил для неизменяемых классов в целом, в пункте 15: "Минимизировать взаимность".

Ответ 10

1) Сделать членов закрытыми и окончательными - так

private Point[] points; //should be 
private final Point[] points;

2) Сделать класс окончательным, чтобы он не подклассифицировался

3) Исключительный доступ к изменяемым элементам (массиву) - означает возвратную копию, а не ссылку на изменяемые элементы

Для лучшего лечения этой темы см. Джошуа Блох, Эффективный Java-элемент 15

Ответ 11

Это может быть лучше Point.   import java.io.Serializable;

public final class Point implements Serializable {
    private static final long serialVersionUID = 0x100;

    private final int x;
    private final int y;
    private final int z;

    public Point(int x, int y, int z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public Point(Point that) {
        this(that.x, that.y, that.z );
    }

    public boolean equals(Object obj) { 
        // assume this is a typical, safe .equals implementation
        // that compares the coordinates in this instance to the
        // other instance
        return true;
    }
}

Ответ 12

Помимо раскрытия массива (поскольку геттеры не хотят делать), а не final, сериализуемое "проблематично".

Как очень противный человек, при десериализации, я могу получить еще одну ссылку на внутренний массив. Очевидное исправление для этого:

private void readObject(
    ObjectInputStream in 
) throws ClassNotFoundException, IOException {
    ObjectInputStream.GetField fields = in.readFields();
    this.points = ((Point[])(fields.get("point", null)).clone();
}

Это все еще оставляет проблему points не final и подвергая объект без points инициализированным (или, что еще хуже, но немного теоретическим, частично инициализированным). То, что вы действительно хотите, - это "последовательный прокси", о котором вы можете узнать в Интернете...

Примечание. Если вы реализуете equals, вы также должны реализовать hashCode, возможно toString и возможный Comparable.

Ответ 13

Сама точка не обязательно должна быть неизменной, чтобы Треугольник был неизменным. Вам просто нужно сделать много защитных копий, чтобы никто не ссылался на объекты Point, хранящиеся в треугольнике.

Кроме того, не должен быть треугольник a-b-c равным треугольником b-c-a (и 4 других перестановок)

Ответ 14

Необязательный пример класса с изменяемым полем:

public  final class  ImmutabilityTest {

    private final int i;
    private final C c1;

    ImmutabilityTest(int i, C c1){
        this.i = i;
        this.c1 = c1;
    }

    public int getI() {
        return i;
    }
    public C getC1() {
        return (C)c1.clone();//If return c1 simply without calling clone then contract of immutable object will break down 
    }

    @Override
    public String toString() {
        return "ImmutabilityTest [i=" + i + ", c1=" + c1 + "]";
    }


    public static void main(String[] args) {

        ImmutabilityTest i1 = new ImmutabilityTest(10, new C(new D("before")));
        System.out.println(i1);
        i1.getC1().getD1().name = "changed";

        System.out.println(i1);

    }

}

class C implements Cloneable{
    D d1;

    public C(D d1) {
        super();
        this.d1 = d1;
    }

    public D getD1() {
        return d1;
    }

    public void setD1(D d1) {
        this.d1 = d1;
    }


    @Override
    public String toString() {
        return "C [d1=" + d1 + "]";
    }

    public C clone(){
        C c = null;
        try {
            c = (C) super.clone();
            c.setD1(c.getD1().clone());// here deep cloning is handled if it is commented it will become shallow cloning
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return c;
    }

}

class D implements Cloneable{
    String name;

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }


    @Override
    public String toString() {
        return "D [name=" + name + "]";
    }

    public D clone(){
        D d = null;
        try {
            d = (D) super.clone();
        } catch (CloneNotSupportedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return d;
    }
}