2

以不变性为例。我如何修改一个对象以表明它已经成为不可变的并且不需要再次包装?

让我们假设我们不想使用反射来扫描设置器,因为那将是低效且不足的。

例子:

// Deliberately chosing lowercase because it is a system attribute.
interface immutable {
  // Nothing in here I can think of.
}

// immute - have I invented a new word?
// What can I do with the return type to indicate immutability?
public static <T> List<T> immute(List<T> list) {
  // If it's not an immutable
  if (!(list instanceof immutable)) {
    // Make it so - how can I stamp it so?
    return Collections.<T>unmodifiableList(list);
  }
  // It is immutable already.
  return list;
}
4

4 回答 4

1

如果检查将在同一位置进行,您可以使用集合或地图,将所有包装对象放置在其中,然后在几乎恒定的时间内检查它们。为避免内存泄漏,您可以使用弱引用来包装它们。

如果 AOP 的引入是一个(相当重量级的)选项,您可以通过AspectJ使用类型间声明来解决您的问题。这样,如果我没记错的话,您可以将一个私有成员与对相应包装实例的引用添加到 Collection 接口:

aspect Unmodifieable {
    private Collection java.util.Collection.unmofifieableWrapper = null;
    public Collection java.util.Collection.getUnmodifieable() {
        if (unmofifieableWrapper == null) {
            unmofifieableWrapper = somehowRetrieveUnmodifieableCollection(this);
        }
        return unmofifieableWrapper;
    }
}
于 2013-04-25T22:50:52.270 回答
1

进一步玩这个想法产生了这个错误的解决方案 - 这太可怕了,几乎任何其他技巧都会更好,但我觉得我应该发布。请找到更好的解决方案:

public class Test {
  // Deliberately chosing lowercase because it is a system attribute.
  interface immutable {
    // Nothing in here I can think of.
  }

  // immute - have I invented a new word?
  // What can I do with the return type to indicate immutability?
  public static <T> List<T> immute(List<T> list) {
    // If it's not an immutable
    if (!(list instanceof immutable)) {
      // Make it so - how can I stamp it so?
      return Hacker.hack(Collections.<T>unmodifiableList(list),
                         List.class,
                         immutable.class);
    }
    // It is immutable already - code DOES get here.
    return list;
  }

  public void test() {
    System.out.println("Hello");
    List<String> test = new ArrayList<>();
    test.add("Test");
    test("Test", test);
    List<String> immutableTest = immute(test);
    test("Immutable Test", immutableTest);
    List<String> immutableImmutableTest = immute(immutableTest);
    test("Immutable Immutable Test", immutableImmutableTest);
  }

  private void test(String name, Object o) {
    System.out.println(name + ":" + o.getClass().getSimpleName() + "=" + o);

  }

  public static void main(String args[]) {
    new Test().test();
  }
}

class Hacker {
  // Hack an object to seem to implement a new interface.
  // New interface should be instanceof testable.
  // Suggest the additional type is an empty interface.
  public static <T> T hack(final Object hack,
                           final Class<T> baseType,
                           final Class additionalType) {

    return (T) Proxy.newProxyInstance(
            Thread.currentThread().getContextClassLoader(),
            new Class[]{baseType, additionalType},
            new InvocationHandler() {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Always invoke the method in the hacked object.
        return method.invoke(hack, args);
      }
    });
  }
}
于 2013-04-25T23:30:50.010 回答
0

您可以使用类中的命名约定来做到这一点。

interface MyObject;
class MyMutableObject implements MyObject;
class MyImmutableObject implements MyObject;

在我目前的项目中,我做了类似的事情。我有一个interface需要设置器的,但其中一个实现类是不可变的。当你调用它的 setter 时,它会抛出一个Exception(它的 setter 永远不应该被调用,但它只是为了安全起见)。

您正在寻找的“信息”更多的是程序员而不是编译器,因此您不需要语言实现的“邮票”。

于 2013-04-25T22:49:34.150 回答
0

Collections.unmodifiable* 方法返回 UnmodifiableCollection 的子类型,因此您可以检查UnmodifiableCollection.class.isAssignableFrom(list)然后测试具体类型。

如果不使用仪器,我认为你会卡在检查类型。

于 2013-04-25T22:50:14.513 回答