有没有办法标记一个对象来表明它已经通过一个过程?

时间:2013-04-25 22:38:48

标签: java immutability

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

让我们假设我们不想使用反射来扫描setter,因为这样效率低且不足。

示例:

// 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 个答案:

答案 0 :(得分:1)

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

如果引入AOP是(相当重量级)选项,您可以通过inter type declarations使用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;
    }
}

答案 1 :(得分: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);
      }
    });
  }
}

答案 2 :(得分:0)

您可以使用类中的命名约定来执行此操作。

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

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

您正在寻找的“信息”对于程序员而言比编译器更多,因此您不需要使用语言实现“标记”。

答案 3 :(得分:0)

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

在不使用仪器的情况下,我认为您已经无法检查类型。