Java 有没有办法标记一个对象以表明它已经经历了一个过程?
以不变性为例。我如何修改一个对象,以表明它已经不可变,不需要再次包装 让我们假设我们不想使用反射来扫描setter,因为这将是低效和不足的 例如:Java 有没有办法标记一个对象以表明它已经经历了一个过程?,java,immutability,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? // Wha
// 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;
}
//故意选择小写,因为它是一个系统属性。
接口不可变{
//我想不出这里有什么。
}
//免疫接种-我发明了一个新单词吗?
//我可以用返回类型做什么来表示不变性?
公共静态列表免疫(列表){
//如果它不是一个不变的
if(!(列出不可变的实例)){
//让它这样-我怎么能这样盖章?
返回集合。不可修改列表(列表);
}
//它已经是不可变的了。
退货清单;
}
您可以使用类中的命名约定来实现这一点
interface MyObject;
class MyMutableObject implements MyObject;
class MyImmutableObject implements MyObject;
在我目前的项目中,我做了类似的事情。我有一个接口
,它需要一个setter,但其中一个实现类是不可变的。当您调用它的setter时,它抛出一个异常
(永远不应该调用它的setter,但它只是为了安全才在那里)
您寻找的“信息”更多的是程序员而不是编译器,因此您不需要语言实现的“戳”。集合。unmodifiable*方法返回UnmodifiableCollection的子类型,以便您可以检查
UnmodifiableCollection.class.isAssignableFrom(list)
,然后测试具体类型
如果不使用检测,我认为您将无法检查类型。如果检查将在同一位置进行,您可以使用集合或地图,在其中放置所有已包装的对象,然后在几乎恒定的时间内检查它们。为了避免内存泄漏,您可以使用 如果引入AOP是一个(相当重要的)选项,那么您可以使用via解决您的问题。通过这种方式,如果我没有记错的话,您可以将一个私有成员与对应的包装实例的引用一起添加到集合接口中:
aspect Unmodifieable {
private Collection java.util.Collection.unmofifieableWrapper = null;
public Collection java.util.Collection.getUnmodifieable() {
if (unmofifieableWrapper == null) {
unmofifieableWrapper = somehowRetrieveUnmodifieableCollection(this);
}
return unmofifieableWrapper;
}
}
进一步玩弄这个想法产生了这个糟糕的解决方案——这是可怕的,几乎任何其他的把戏都会更好,但我觉得我应该发帖。请请找到更好的解决方案:
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);
}
});
}
}
公共类测试{
//故意选择小写,因为它是系统属性。
接口不可变{
//我想不出这里有什么。
}
//免疫接种-我发明了一个新单词吗?
//我可以用返回类型做什么来表示不变性?
公共静态列表免疫(列表){
//如果它不是一个不变的
if(!(列出不可变的实例)){
//让它这样-我怎么能这样盖章?
返回Hacker.hack(集合.不可修改列表(列表)),
List.class,
不变类);
}
//它已经是不可变的了——代码确实在这里。
退货清单;
}
公开无效测试(){
System.out.println(“你好”);
列表测试=新建ArrayList();
测试。添加(“测试”);
测试(“测试”,测试);
List immutableTest=免疫(测试);
测试(“不可变测试”,不可变测试);
List immutableImmutableTest=免疫接种(immutableTest);
测试(“不可变不可变测试”,不可变不可变测试);
}
私有无效测试(字符串名称,对象o){
System.out.println(name+“:”+o.getClass().getSimpleName()+“=”+o);
}
公共静态void main(字符串参数[]){
新测试().Test();
}
}
类黑客{
//攻击一个对象,使其看起来实现了一个新接口。
//新接口应该是可测试的实例。
//建议附加类型为空接口。
公共静态T黑客(最终对象黑客,
最终类基类,
最终类(附加类型){
return(T)Proxy.newProxyInstance(
Thread.currentThread().getContextClassLoader(),
新类[]{baseType,additionalType},
新的调用处理程序(){
@凌驾
公共对象调用(对象代理、方法、对象[]args)抛出Throwable{
//始终在被黑客攻击的对象中调用该方法。
返回方法.invoke(hack,args);
}
});
}
}
抛出不受支持的操作或必须实现从未调用的方法是一种糟糕的代码味道。您应该有两个接口,第二个接口有setter,只有可变类实现了这一点(第二个接口可以扩展第一个接口)。UnmodifiableCollection
不可见,所以他需要这样做:class.forName(“java.util.Collections$UnmodifiableCollection”).isInstance(list)
@Lonenebula,+1你说得很对,这使得类型检查的味道更糟!还有许多其他不可变的类型需要检查——不是答案,而是值得注意的。您必须跟踪(使用一些集合)、标记(使用工具)或检查(预先知道所有类型)。最好改变你的设计,让你知道它是不可变的——说起来容易做起来难。我喜欢这个想法——但我真的想以某种方式标记这个对象,而不是仅仅把它记录在被黑客攻击的对象的分类账中。还有一个玩AspectJ的原因——我已经找了很久的借口了。:)toString()
有效吗?似乎proxy.toString()
被转发到hack.toString()
您必须确保不存在对被代理的原始对象的其他引用-如果您可以这样做,那么您可以确保执行不可变转换,并且只执行不可变转换once@zhong.j.yu-不-似乎串不起作用-我已经移除了它,因为它只是一种干扰。@earcam-很好的观点-但不是我的问题如果我知道这是不必要的,我只想不重新包装。我真的很讨厌把它作为答案。似乎没有任何合理的解释