Java 我应该在不可变类的变异尝试中引发异常吗?如果有,哪一个例外?

Java 我应该在不可变类的变异尝试中引发异常吗?如果有,哪一个例外?,java,exception,immutability,Java,Exception,Immutability,我希望在开发人员试图改变不可变对象时提醒他。不可变对象实际上是可变对象的扩展,它覆盖所述对象上的setter使其不可变 可变基类:Vector3 public class Vector3 { public static final Vector3 Zero = new ImmutableVector3(0, 0, 0); private float x; private float y; private float z; public Vector3(

我希望在开发人员试图改变不可变对象时提醒他。不可变对象实际上是可变对象的扩展,它覆盖所述对象上的setter使其不可变

可变基类:Vector3

public class Vector3 {
    public static final Vector3 Zero = new ImmutableVector3(0, 0, 0);

    private float x;
    private float y;
    private float z;

    public Vector3(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public void set(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}
public final class ImmutableVector3 extends Vector3 {
    public ImmutableVector3(float x, float y, float z) {
        super(x, y, z);
    }

    //Made immutable by overriding any set functionality.
    @Override
    public void set(float x, float y, float z) {
        throw new Exception("Immutable object."); //Should I even throw an exception?
    }
}
不可变版本:不可变向量3

public class Vector3 {
    public static final Vector3 Zero = new ImmutableVector3(0, 0, 0);

    private float x;
    private float y;
    private float z;

    public Vector3(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }

    public void set(float x, float y, float z) {
        this.x = x;
        this.y = y;
        this.z = z;
    }
}
public final class ImmutableVector3 extends Vector3 {
    public ImmutableVector3(float x, float y, float z) {
        super(x, y, z);
    }

    //Made immutable by overriding any set functionality.
    @Override
    public void set(float x, float y, float z) {
        throw new Exception("Immutable object."); //Should I even throw an exception?
    }
}
我的用例如下所示:

public class MyObject {
    //position is set to a flyweight instance of a zero filled vector.
    //Since this is shared and static, I don't want it mutable.
    private Vector3 position = Vector3.Zero;
}
假设我团队中的一名开发人员决定他需要操纵对象的位置,但目前它被设置为静态、不可变的Vector3.Zero共享向量实例

如果他提前知道如果位置向量设置为Vector3的共享实例,他需要创建可变向量的新实例,这将是最好的。零:

if (position == Vector3.Zero)
    position = new Vector3(x, y, z);
但是,假设他没有先检查这个。我认为,在这种情况下,最好抛出一个异常,如上面的
ImmutableVector3.set(x,y,z)
方法中所示。我想使用一个标准的Java异常,但我不确定哪一个最合适,我也不是100%确信这是处理这个问题的最佳方法

我在(这个问题)上看到的建议[非法状态例外适用于不可变对象吗?似乎建议如下:

  • IllegalStateException——但它是一个不可变的对象,因此只有一个状态
  • UnsupportedOperationException-不支持
    set()
    方法,因为它重写基类,因此这可能是一个不错的选择
  • NoTouchElementException-我不确定这是一个好的选择,除非该方法被视为
    元素
此外,我甚至不确定是否应该在这里抛出异常。我不能简单地更改对象,也不能提醒开发人员他们正在尝试更改不可变对象,但这似乎也很麻烦。在
ImmutableVector3.set(x,y,z)中抛出异常的问题
是指
set
必须在
ImmutableVector3
Vector3
上引发异常。因此,现在,即使
Vector3.set(x,y,z)
永远不会引发异常,它也必须被放置在
try/catch
块中

问题
  • 除了抛出异常,我是否忽略了另一个选项
  • 如果例外是最好的方式,我应该选择哪种例外

    • 抛出异常通常是一种方法,您应该使用

      Java API本身确实在以下方面提出了建议:

      此接口中包含的“破坏性”方法,即修改其操作的集合的方法,指定在该集合不支持该操作时引发UnsupportedOperationException

      如果创建完整的类层次结构,另一种方法是创建一个超类
      向量
      ,该超类不可修改,没有像
      set()
      这样的修改方法,以及两个子类
      不可变向量
      (保证不可变)和
      可变向量
      (可修改)。这会更好,因为您获得了编译时安全性(您甚至无法尝试修改
      ImmutableVector
      ),但根据具体情况,可能会过度设计(最终可能会产生许多类)

      例如,选择这种方法是因为,它们都存在三种变体

      在所有需要修改向量的情况下,都使用类型
      MutableVector
      。在所有不关心修改并且只想从中读取的情况下,都只需使用
      vector
      。在所有需要保证不变性的情况下(例如,
      Vector
      实例被传递到构造函数中,您希望确保以后不会有人修改它)您使用
      ImmutableVector
      (它通常会提供一个复制方法,所以您只需调用
      ImmutableVector.copyOf(Vector)
      )。不需要从
      Vector
      向下转换,请指定所需的正确子类型作为类型。此解决方案的要点(需要更多代码)是关于类型安全和编译时检查,因此向下转换会破坏这一优势。不过也有例外,例如
      ImmutableVector.copyOf()
      方法作为一种优化,通常如下所示:

      public static ImmutableVector copyOf(Vector v) {
        if (v instanceof ImmutableVector) {
          return (ImmutableVector)v;
        } else {
          return new ImmutableVector(v);
        }
      }
      
      这仍然是安全的,并且为您提供了不可变类型的另一个好处,即避免了不必要的复制

      有大量遵循这些模式的实现(尽管它们仍然扩展了可变标准Java集合接口,因此不提供编译时安全性)。您应该阅读它们的描述,以了解有关不可变类型的更多信息


      第三种选择是只使用不可变向量类,而不使用可变类。通常出于性能原因使用可变类是一种过早的优化,如(由于采用了分代垃圾收集器,对象分配非常便宜,而且在最好的情况下,对象清理甚至可以不花费任何成本)。当然,对于非常大的对象(如列表),这不是解决方案,但对于三维向量,这肯定是一个选项(可能是最好的组合,因为它简单、安全,并且比其他组合需要更少的代码)。我不知道Java API中有任何这种替代方法的例子,因为在创建API的大部分部分时,VM没有得到优化,太多的对象可能是一个性能问题。但这不应该阻止您今天这样做。

      如果您尝试变异不可变的cl,您应该抛出一个编译器错误屁股


      不可变类不应该扩展可变类。

      鉴于您的情况,我认为您应该抛出一个
      不支持操作异常

      package com.sandbox;
      
      import java.io.IOException;
      import java.util.ArrayList;
      import java.util.Collections;
      import java.util.List;
      
      public class Sandbox {
      
          public static void main(String[] args) throws IOException {
              List<Object> objects = Collections.unmodifiableList(new ArrayList<Object>());
              objects.add(new Object());
          }
      
      }
      
      package.com.sandbox;
      导入java.io.IOException;
      导入java.util.ArrayList;
      导入java.util