¿Cómo crear objetos inmutables en Java?
¿Qué objetos deberían llamarse inmutables?
Si tengo una clase con todos los miembros estáticos, ¿es inmutable?
¿Cómo crear objetos inmutables en Java?
¿Qué objetos deberían llamarse inmutables?
Si tengo una clase con todos los miembros estáticos, ¿es inmutable?
Respuestas:
A continuación se presentan las duras exigencias de un objeto inmutable.
final
pero el objeto aún puede ser mutable private final Date imStillMutable
. Deberías hacer defensive copies
en estos casos.El razonamiento detrás de hacer la clase final
es muy sutil y a menudo se pasa por alto. Si no es el final, las personas pueden extender libremente su clase, anular public
o protected
comportarse, agregar propiedades mutables y luego proporcionar su subclase como sustituto. Al declarar la clase final
, puede asegurarse de que esto no suceda.
Para ver el problema en acción, considere el siguiente ejemplo:
public class MyApp{
/**
* @param args
*/
public static void main(String[] args){
System.out.println("Hello World!");
OhNoMutable mutable = new OhNoMutable(1, 2);
ImSoImmutable immutable = mutable;
/*
* Ahhhh Prints out 3 just like I always wanted
* and I can rely on this super immutable class
* never changing. So its thread safe and perfect
*/
System.out.println(immutable.add());
/* Some sneak programmer changes a mutable field on the subclass */
mutable.field3=4;
/*
* Ahhh let me just print my immutable
* reference again because I can trust it
* so much.
*
*/
System.out.println(immutable.add());
/* Why is this buggy piece of crap printing 7 and not 3
It couldn't have changed its IMMUTABLE!!!!
*/
}
}
/* This class adheres to all the principles of
* good immutable classes. All the members are private final
* the add() method doesn't modify any state. This class is
* just a thing of beauty. Its only missing one thing
* I didn't declare the class final. Let the chaos ensue
*/
public class ImSoImmutable{
private final int field1;
private final int field2;
public ImSoImmutable(int field1, int field2){
this.field1 = field1;
this.field2 = field2;
}
public int add(){
return field1+field2;
}
}
/*
This class is the problem. The problem is the
overridden method add(). Because it uses a mutable
member it means that I can't guarantee that all instances
of ImSoImmutable are actually immutable.
*/
public class OhNoMutable extends ImSoImmutable{
public int field3 = 0;
public OhNoMutable(int field1, int field2){
super(field1, field2);
}
public int add(){
return super.add()+field3;
}
}
En la práctica, es muy común encontrar el problema anterior en entornos de inyección de dependencia. No está instanciando cosas explícitamente y la referencia de superclase que se le da puede ser en realidad una subclase.
La conclusión es que para ofrecer garantías estrictas sobre la inmutabilidad, debes marcar la clase como final
. Esto se trata en profundidad en Effective Java de Joshua Bloch y se hace referencia explícitamente en la especificación del modelo de memoria de Java .
Simplemente no agregue métodos públicos mutadores (establecedores) a la clase.
private
o la clase debería serlo final
. Solo para evitar la herencia. Porque la herencia viola la encapsulación.
Las clases no son inmutables, los objetos sí.
Medios inmutables: mi estado visible público no puede cambiar después de la inicialización.
Los campos no tienen que declararse definitivos, aunque puede ayudar enormemente a garantizar la seguridad de los subprocesos.
Si su clase solo tiene miembros estáticos, entonces los objetos de esta clase son inmutables, porque no puede cambiar el estado de ese objeto (probablemente tampoco pueda crearlo :))
Para hacer que una clase sea inmutable en Java, puede tomar nota de los siguientes puntos:
1. No proporcione métodos de establecimiento para modificar valores de ninguna de las variables de instancia de la clase.
2. Declare la clase como 'final' . Esto evitaría que cualquier otra clase la extienda y, por lo tanto, anule cualquier método que pueda modificar los valores de las variables de instancia.
3. Declare las variables de instancia como privadas y finales .
4. También puede declarar el constructor de la clase como privado y agregar un método de fábrica para crear una instancia de la clase cuando sea necesario.
¡Estos puntos deberían ayudar!
private
?
De oráculo sitio, cómo crear objetos inmutables en Java.
- No proporcione métodos "establecedores": métodos que modifican campos u objetos a los que se hace referencia por campos.
- Haga que todos los campos sean finales y privados.
- No permita que las subclases anulen métodos. La forma más sencilla de hacer esto es declarar la clase como final. Un enfoque más sofisticado es hacer que el constructor sea privado y construir instancias en métodos de fábrica.
- Si los campos de instancia incluyen referencias a objetos mutables, no permita que esos objetos se cambien:
I. No proporcione métodos que modifiquen los objetos mutables.
II. No comparta referencias a los objetos mutables. Nunca almacene referencias a objetos externos mutables pasados al constructor; si es necesario, cree copias y almacene las referencias a las copias. Del mismo modo, cree copias de sus objetos mutables internos cuando sea necesario para evitar devolver los originales en sus métodos.
Un objeto inmutable es un objeto que no cambiará su estado interno después de la creación. Son muy útiles en aplicaciones multiproceso porque se pueden compartir entre subprocesos sin sincronización.
1. No agregue ningún método setter
Si está construyendo un objeto inmutable, su estado interno nunca cambiará. La tarea de un método de establecimiento es cambiar el valor interno de un campo, por lo que no puede agregarlo.
2. Declarar todos los campos definitivos y privados.
Un campo privado no es visible desde fuera de la clase, por lo que no se le pueden aplicar cambios manuales.
Declarar un campo final garantizará que si hace referencia a un valor primitivo, el valor nunca cambiará si hace referencia a un objeto, la referencia no se puede cambiar. Esto no es suficiente para garantizar que un objeto con solo campos finales privados no sea mutable.
3. Si un campo es un objeto mutable, cree copias defensivas de él para los métodos getter
Hemos visto antes que definir un campo final y privado no es suficiente porque es posible cambiar su estado interno. Para resolver este problema, necesitamos crear una copia defensiva de ese campo y devolver ese campo cada vez que se solicite.
4. Si un objeto mutable pasado al constructor debe asignarse a un campo, cree una copia defensiva del mismo.
El mismo problema ocurre si mantiene una referencia pasada al constructor porque es posible cambiarla. Por lo tanto, mantener una referencia a un objeto pasado al constructor puede crear objetos mutables. Para solucionar este problema es necesario crear una copia defensiva del parámetro si son objetos mutables.
Nótese que si un campo es una referencia a un objeto inmutable no es necesario crear copias defensivas del mismo en el constructor y en los métodos getter basta con definir el campo como final y privado.
5. No permita que las subclases anulen métodos
Si una subclase anula un método, puede devolver el valor original de un campo mutable en lugar de una copia defensiva del mismo.
Si sigue esas reglas simples, puede compartir libremente sus objetos inmutables entre subprocesos porque son seguros para subprocesos.
No hay bien o mal, solo depende de lo que prefieras. Solo depende de su preferencia y de lo que quiera lograr (y poder usar fácilmente ambos enfoques sin alienar a los fanáticos acérrimos de un lado u otro es un santo grial que algunos idiomas buscan).
En primer lugar, sabe por qué necesita crear un objeto inmutable y cuáles son las ventajas del objeto inmutable.
Ventajas de un objeto inmutable
Concurrencia y subprocesos múltiples Es automáticamente seguro para subprocesos, por lo que se produce un problema de sincronización ... etc.
No es necesario copiar el constructor. No es necesario implementar el clon. La clase no se puede anular. Hacer que el campo sea privado y final. Forzar a los llamadores a construir un objeto completamente en un solo paso, en lugar de usar un constructor sin argumentos.
Los objetos inmutables son simplemente objetos cuyo estado significa que los datos del objeto no pueden cambiar después de que se construye el objeto inmutable.
por favor vea el código a continuación.
public final class ImmutableReminder{
private final Date remindingDate;
public ImmutableReminder (Date remindingDate) {
if(remindingDate.getTime() < System.currentTimeMillis()){
throw new IllegalArgumentException("Can not set reminder" +
" for past time: " + remindingDate);
}
this.remindingDate = new Date(remindingDate.getTime());
}
public Date getRemindingDate() {
return (Date) remindingDate.clone();
}
}
Minimizar la mutabilidad
Una clase inmutable es simplemente una clase cuyas instancias no se pueden modificar. Toda la información contenida en cada instancia se proporciona cuando se crea y se fija durante la vida útil del objeto.
Clases inmutables de JDK: String, las clases primitivas en caja (clases contenedoras), BigInteger y BigDecimal, etc.
¿Cómo hacer inmutable una clase?
Haz copias defensivas. Garantice el acceso exclusivo a cualquier componente mutable.
public List getList () {return Collections.unmodifiableList (lista); <=== copia defensiva del campo mutable antes de devolverlo a la persona que llama}
Si su clase tiene campos que se refieren a objetos mutables, asegúrese de que los clientes de la clase no puedan obtener referencias a estos objetos. Nunca inicialice dicho campo en una referencia de objeto proporcionada por el cliente ni devuelva la referencia de objeto de un descriptor de acceso.
import java.util.Date;
public final class ImmutableClass {
public ImmutableClass(int id, String name, Date doj) {
this.id = id;
this.name = name;
this.doj = doj;
}
private final int id;
private final String name;
private final Date doj;
public int getId() {
return id;
}
public String getName() {
return name;
}
/**
* Date class is mutable so we need a little care here.
* We should not return the reference of original instance variable.
* Instead a new Date object, with content copied to it, should be returned.
* */
public Date getDoj() {
return new Date(doj.getTime()); // For mutable fields
}
}
import java.util.Date;
public class TestImmutable {
public static void main(String[] args) {
String name = "raj";
int id = 1;
Date doj = new Date();
ImmutableClass class1 = new ImmutableClass(id, name, doj);
ImmutableClass class2 = new ImmutableClass(id, name, doj);
// every time will get a new reference for same object. Modification in reference will not affect the immutability because it is temporary reference.
Date date = class1.getDoj();
date.setTime(date.getTime()+122435);
System.out.println(class1.getDoj()==class2.getDoj());
}
}
Para obtener más información, consulte mi blog:
http://javaexplorer03.blogspot.in/2015/07/minimize-mutability.html
un objeto se llama inmutable si su estado no se puede cambiar una vez creado. Una de las formas más simples de crear una clase inmutable en Java es estableciendo que todos sus campos sean finales. Si necesita escribir una clase inmutable que incluya clases mutables como "java.util.Date". Para preservar la inmutabilidad en tales casos, se aconseja devolver copia del objeto original,
Los objetos inmutables son aquellos objetos cuyo estado no se puede cambiar una vez que se crean, por ejemplo, la clase String es una clase inmutable. Los objetos inmutables no se pueden modificar, por lo que también son seguros para subprocesos en ejecución simultánea.
Características de las clases inmutables:
Claves para escribir clase inmutable:
Deben tenerse en cuenta los siguientes pasos cuando desee que cualquier clase sea una clase inmutable.
Echemos un vistazo a lo que hemos escrito arriba:
//ImmutableClass
package younus.attari;
public final class ImmutableExample {
private final String name;
private final String address;
public ImmutableExample(String name,String address){
this.name=name;
this.address=address;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
}
//MainClass from where an ImmutableClass will be called
package younus.attari;
public class MainClass {
public static void main(String[] args) {
ImmutableExample example=new ImmutableExample("Muhammed", "Hyderabad");
System.out.println(example.getName());
}
}
Además de la respuesta proporcionada por @ nsfyn55, los siguientes aspectos también deben tenerse en cuenta para la inmutabilidad del objeto, que son de suma importancia
Considere las siguientes clases:
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = mc;
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
public class MutabilityCheck {
public static void main(String[] args) {
MutableClass mc = new MutableClass();
mc.setName("Foo");
ImmutableClass iMC = new ImmutableClass(mc);
System.out.println(iMC.getMutClass().getName());
mc.setName("Bar");
System.out.println(iMC.getMutClass().getName());
}
}
Lo siguiente será el resultado de MutabilityCheck:
Foo
Bar
Es importante observar que,
Construir objetos mutables en un objeto inmutable (a través del constructor), ya sea 'copiando' o 'agrupando' a variables de instancia de lo inmutable descrito por los siguientes cambios:
public final class ImmutableClass {
private final MutableClass mc;
public ImmutableClass(MutableClass mc) {
this.mc = new MutableClass(mc);
}
public MutableClass getMutClass() {
return this.mc;
}
}
public class MutableClass {
private String name;
public MutableClass() {
}
//copy constructor
public MutableClass(MutableClass mc) {
this.name = mc.getName();
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
todavía no asegura la inmutabilidad completa ya que lo siguiente sigue siendo válido de la clase MutabilityCheck:
iMC.getMutClass().setName("Blaa");
Sin embargo, ejecutar MutabilityCheck con los cambios realizados en 1. dará como resultado que el resultado sea:
Foo
Foo
Para lograr una inmutabilidad completa en un objeto, todos sus objetos dependientes también deben ser inmutables.
Desde JDK 14+ que tiene JEP 359 , podemos usar " records
". Es la forma más sencilla y sin complicaciones de crear una clase inmutable.
Una clase de registro es un portador transparente y poco inmutable para un conjunto fijo de campos conocido como el registro components
que proporciona una state
descripción para el registro. Cada uno component
da lugar a un final
campo que contiene el valor proporcionado y un accessor
método para recuperar el valor. El nombre del campo y el nombre del descriptor de acceso coinciden con el nombre del componente.
Consideremos el ejemplo de crear un rectángulo inmutable.
record Rectangle(double length, double width) {}
No es necesario declarar ningún constructor, no es necesario implementar equals
& hashCode
métodos. Cualquier registro necesita un nombre y una descripción del estado.
var rectangle = new Rectangle(7.1, 8.9);
System.out.print(rectangle.length()); // prints 7.1
Si desea validar el valor durante la creación del objeto, tenemos que declarar explícitamente el constructor.
public Rectangle {
if (length <= 0.0) {
throw new IllegalArgumentException();
}
}
El cuerpo del registro puede declarar métodos estáticos, campos estáticos, inicializadores estáticos, constructores, métodos de instancia y tipos anidados.
Métodos de instancia
record Rectangle(double length, double width) {
public double area() {
return this.length * this.width;
}
}
campos estáticos, métodos
Dado que el estado debe ser parte de los componentes, no podemos agregar campos de instancia a los registros. Pero podemos agregar campos y métodos estáticos:
record Rectangle(double length, double width) {
static double aStaticField;
static void aStaticMethod() {
System.out.println("Hello Static");
}
}