"将一个不可变的接口返回到原始数据.然后你可以更改对象中的字段,但调用者不能除非他通过强制转换作弊.你只公开你希望用户拥有的方法.对类进行相同操作比较复杂子类必须公开其超类所做的一切"
他的意思是你可以作弊,为什么对于子类来说这很棘手?
您提供的interface没有突变方法.然后,您提供仅为创建者所知的可变实现.
public interface Person
{
String getName();
}
public class MutablePerson implements Person
{
private String name;
public MutablePerson(String name)
{
this.name = name;
}
@Override
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
}
Run Code Online (Sandbox Code Playgroud)
此时,如果你Person在任何地方都返回对象,那么某人修改返回对象的唯一方法就是作弊并将其强制转换为MutablePerson.实际上,除非代码是完整的hack,否则可变对象变为不可变.
Person person = new MutablePerson("picky");
// someone is cheating:
MutablePerson mutableAgain = (MutablePerson)person;
mutableAgain.setName("Phoenix");
// person.getName().equals("Phoenix") == true
Run Code Online (Sandbox Code Playgroud)
如果不与一群年轻的程序员打交道会注意到真正的实现是可变的,因此他们可以强制改变它,那么你提供了不变性的安全性,并且能够将它组合在一起而不需要无限的构造函数,或使用Builder(实际上,可变版本是Builder).避免开发人员滥用可变版本的好方法是将可变版本作为包私有,以便只有包知道它.这个想法的否定之处在于,只有在相同的包中实例化它才有效,这可能是这种情况,但在DAO与多个包定义的实现一起使用的情况下显然可能不是这种情况(例如,MySQL,Oracle,Hibernate,Cassandra等,都返回相同的东西,并希望彼此分开,以避免混乱他们的包).
这里真正的关键是人们不应该从Mutable对象构建,除了实现进一步向下的接口.如果你正在扩展,然后返回一个不可变的子类,那么根据定义,如果它公开了一个可变对象,它就不是不可变的.例如:
public interface MyType<T>
{
T getSomething();
}
public class MyTypeImpl<T> implements MyType<T>
{
private T something;
public MyTypeImpl(T something)
{
this.something = something;
}
@Override
public T getSomething()
{
return something;
}
public void setSomething(T something)
{
this.something = something;
}
}
public interface MyExtendedType<T> extends MyType<T>
{
T getMore();
}
public class MyExtendedTypeImpl<T>
extends MyTypeImpl<T>
implements MyExtendedType<T>
{
private T more;
public MyExtendedTypeImpl(T something, T more)
{
super(something);
this.more = more;
}
@Override
public T getMore()
{
return more;
}
public void setMore(T more)
{
this.more = more;
}
}
Run Code Online (Sandbox Code Playgroud)
老实说,这Collection应该是Java应该实现的方式.只读接口可以代替Collections.unmodifiable实现,因此不会让人意外地使用可变对象的不可变版本.换句话说,你永远不应该隐藏不变性,但你可以隐藏可变性.
然后,他们可以撒上真正无法修改的不可变实例,这将使开发人员保持诚实.同样,我可能希望在某处看到上面接口的不可变版本(名称更好):
public class MyTypeImmutable<T> implements MyType<T>
{
private final T something;
public MyTypeImmutable(T something)
{
this.something = something;
}
@Override
public T getSomething()
{
return something;
}
}
Run Code Online (Sandbox Code Playgroud)