Java: cambiar el valor de un atributo privado de un objeto
En Java a veces es necesario cambiar el valor de un atributo privado de un objeto. Por suerte, la reflexión nos permite hace esto de forma sencilla. El código, como vereis, no puede ser más simple:
public static void setField (Object o, String fieldName, Object newValue) throws IllegalArgumentException, IllegalAccessException { // Get all the object class fields final Field fields[] = o.getClass().getDeclaredFields(); // Search the requested field for (int i = 0; i < fields.length; ++i) // If found, set its new value if (fieldName.equals(fields[i].getName())) { boolean accesible = fields[i].isAccessible(); fields[i].setAccessible(true); fields[i].set(o, newValue); fields[i].setAccessible(accesible); } }
Un sencillo código para hacer la prueba puede ser el siguiente, que juega con el atributo privado hash de la clase String:
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException { String s = new String("abc"); System.out.println("hashcode: " + s.hashCode()); setField(s, "hash", 30); System.out.println("hashcode: " + s.hashCode()); }
Parecidos razonables
-
Java Fixed Hashtable (tabla hash de longitud fija en Java)
May 11, 2007
14 -
Grails – generic methods for equals and hashCode calculation
December 27, 2012
3 -
Servicios web (2): Desplegando un servicio JWS
June 12, 2006
4 -
Groovy – Simplest way of resolving a nested property
July 24, 2013
4 -
Servicios web (3): Desplegando un servicio WDSS
June 12, 2006
7
Que no te oigan eso en la facultad, que te quitan 40 créditos de golpe.
No, en serio, los campos privados son privados por decisión del programador de la clase, y de hecho, no debían ni ser conocidos por nombre. El API de reflexión de Java no debería ni informarte de su existencia.
Además, si la clase de la que quieres acceder a un campo privado la has programado tú (cosa más que probable, ya que conoces el nombre y el significado de tal campo privado), quizá lo que debas plantearte es re-pensarla para que ese campo privado sea modificable por las clases externas (sobre todo es más seguro, ya que tú, como diseñador de la clase, tendrás el control sobre como los usuarios modifican tal campo). Sólo mi opinión.
Todo lo que has dicho es cierto, salvo en el caso de que el software quiera reflexionar sobre su propia arquitectura y decida que el diseño del programador es malo… De ese modo puede cambiar su comportamiento dinámicamente sin necesidad de recompilar 😉
Eso si, lo mejor es que la parte de “no matar humanos” se guarde en un sitio a parte… No volátil ni modificable… 😀
Jejeje es una bonita opinión, pero demasiado teórica. Si no conoces el problema es difícil opinar, y esto no lo digo como crítica, ya que si yo leyese mi entrada siendo otra persona ( ❓ ) también pensaría lo mismo que tú, y yo no he dado detalles sobre el problema.
La frase (a veces es necesario cambiar…) no la he pensado mucho, era una forma de introducir la cuestión 😉 .
La clase cuyo campo voy a modificar no la conozco, es proporcionada a mi sistema por el cliente. Necesito poder escrbir sus campos privados ya que la granularidad en escritura de mi sistema transaccional es por atributo en lugar de usar todo el objeto. Al compromter los cambios, una transacción que haya escrito el objeto a comprometer debe escribir en la copia actual sólo los atributos que haya escrito en su copia privada. La manera de saber qué campos modificará cada método se hace (hará) analizando el código o mediante anotaciones, de manera que toda esa información permita automaticidad.
No sé si me he explicado bien o si ni siquiera te interesa 😆 pero bueno, siéntete libre de preguntar lo que quieras (¡toma anglicismo 😆 !)
PD: SPQR eres un friki aunque quieras hacer creer a Pina que no. Luego dices del pobre Yosua…
Yo nunca he negado tener un cierto nivel de frikismo… De ahí que me lleve bien con frikis auténticos como tu 😛
Y tampoco intento hacer creer nada a Pina: ella me quiere tal y como soy, sea lo que sea lo que soy. Lo que si que se es lo que no soy: no soy tan friki como Yosua… I también se que tengo razón… o no. 😆
Me hace falta la foto más de lejos. ¿Estamos hablando de algo que tiene que ver con objetos distribuidos?
No, nada que ver con objetos distribuidos. Es un sistema transaccional centralizado (pero que debe soportar concurrencia – cada transacción es ejecutada por un hilo) en el que los datos son los objetos y que usa el sistema de copia privada para las escrituras (una transacción que escribe un objeto obtiene una copia del mismo con la que trabaja, y si tiene exito y compromete, sustituye la copia pública por su copia privada, mientras que si sólo lee el objeto usa la copia pública). Lo que quiero hacer es que a la hora de comprometer sólo se escriban en la copia pública los campos de la copia privada que han sido modificados, y dicha escritura la debe hacer el sistema transaccional, ya que el objeto es proporcionado por el cliente.
¡Qué poco me conoces!
Vamos a ver, que me entere, tú estás programando el sistema de transacciones, y el “usuario programador” te dará las clases que debes “transaccionar” (como me gusta inventar palabros). En ese caso, si quieres guardar todo el estado de una clase para recrearla más tarde, tú solución me parece válida (al fin y al cabo la serialización de Java tiene que hacer lo mismo). Si es ese el supuesto no estás “modificando” los datos privados, estás leyendolos y luego guardandolos, no me parece que en ese caso te metas demasiado con la clase del “usuario” (siempre que no te dediques a juguetear con los datos entre medias).
Sigo sin conocer el problema, pero posiblemente mi solución sería ser un déspota y obligar a las clases que quieran pasar por mi sistema transaccional a implementar cierta interfaz ITransactionable o similar.
Jajaja sé seguro que te interesa 😎 pero es que sonaba muy mal decirlo. Pero bueno, ya te has encargado tú de dejar claro que te interesa seguro porque eres un adicto a los problemas de
ingeniososingenieros, no porque lo que yo hago sea muy interesante.Correcto.
No estoy seguro de a qué te refieres. ¿A hacer la copia quizá? Para ello utilizo una operación clone, y si no está disponible serialización. Mi solución sí que urga en los datos privados del objeto, aunque es lo que se espera que haga. Intentaré explicarlo mejor:
La transacción T1 abre para escritura el objecto O1 para escribir el atributo A1. Se crea una copia de O1, O1′, que es la que usa T1. Si la transacción T2 abre para escritura el objecto O1 para escribir el atributo A2, si la granularidad fuese a nivel de objeto tendríamos un conflicto: T1 y T2 escriben el mismo dato. Sin embargo, para reducir la contención, hacemos que la granularidad baje a nivel de atributo, con lo que ya no hay conflicto en este caso.
A la hora de comprometer, si T1 lo hace sustituyendo la copia pública por la suya privada y posteriormente T2 hace lo mismo, la copia pública sólo contendría las modificaciones que hizo T2. Por eso, en lugar de sustituir la copia pública, se sustituyen los atributos de la misma que fueron modificados por la transacción que compromete. Para modificar dichos atributos se usa mi solución.
Eso reduciría las posibilidades de uso a casos muy concretos. En principio la idea es que las transacciones pasen a formar parte del día a día del programador “de a pié”, ya que a pesar de que los procesadores aumentan su nivel de paralelismo día tras día, las aplicaciones no se diseñan para aprovechar esta característica. Esto es debido a que programar con concurrencia es difícil con las soluciones tradicionales (cerrojos de grano fino por ejemplo). Por ello el diseño ha de ser lo más automatizable posible (generadores de código a partir de anotaciones por ejemplo) y lo más genérico posible.
Según “mi” definición, tu solución no “urga” en los objetos. Tu transacción mira el valor de un atributo, lo guarda, lo compara, luego lo escribe, pero nunca lo modifica a un valor que el usuario no hubiera puesto ya allí.
Por supuesto mi solución era una solución “académica” (lease rebuscada, poco útil, que se dejará de utilizar por algo menos complicado), pero despues de tantos años en la carrera son las únicas que se me ocurren. Sabía que no era válida para tu caso, ya que en tu solución no requieres al usuario nada de trabajo, cosa que cualquier usuario agradecerá.
Joe te explicas como un libro abierto, no como yo que estoy más bien entornado (aunque para ser estrictos, el usuario no habría puesto ese valor porque es un atributo privado, habría sido un método del propio objeto 😉 ). Eso es a lo que me refería, que no lo hace para modificar el estado del objecto.
Por cierto que el sistema no está destinado a ningún usuario, sino que su propósito es supuestamente académico: publicar artículos en congresos 😛 .
En mi ignorancia como programador desconocía que se pudiesen cambiar los atributos privados de un objeto.
¿Por qué existe algo así?
Como diría Nelson, y ¿que-e-e paaa-sa si ese mago que cambia las cosas pierde su varita?
Porque se me ocurre que por diseño esto tiene que hacer uso de memoria compartida. Bueno, claro, que las clases son prestadas a la centralita.
Ya me he enterado. A ti te prestan, te actualizas y pones al día a los demas.
¿Es cómo un control de versiones transaccional?
Exacto (siento no haberte contestado hasta ahora, no vi tu comentario 😥 ). El sistema transaccional usa versiones (es snapshot isolation, y quien quiera saber más a este respecto que me pida el TFC dentro de un par de meses 😉 o mire la Wikipedia) para poder hacer el rollback y para construir un snapshot consistente.
Today good day 🙂
The interesting name of a site – blog.deigote.com
I today 7 hours
looked in the Internet So I have found your site 🙂
The interesting site but does not suffice several sections!
However this section is very necessary!
Best wishes for you!
Forgive I is drunk :))
😯
¿Y éste?
Perdónale, hombre, ¿no ves que borracho ir él? si es que eres un intransigente 🙄 …
Depression Depression Depression aaaaaaaa
HEEEEELP 🙁 🙁 🙁
I hate winter! I want summer!
😯
Pero tío… ¿qué le pasa a la gente con este post?
La reflexión en Java tiene consecuencias impredecibles en el cerebro humano…
La gente, que lee reflexión y se ponen profundos 🙄
xDDDD
I very much love summer 🙂
Someone very much loves winter 🙁
I Wish to know whom more 🙂
For what you love winter?
For what you love summer? Let’s argue 🙂
¡Anda venga!
¡Esto es siempre el mismo!
De hecho empiezo a pensar que eres tu Deigote, que haces lo que sea para darle vidilla a tu blog (menos nuevos posts :P)
Your comments are really helpful. ,
Mine?! What about yours?? Awesome ❗ ❗ ❗
Le … wait for it … gendary!
Este post tiene algo! xDD
es un buen ejemplo pero … como haria para conocer el valor de ese campo ahorita hice lo mismo pera conocer el valor pero si me sigue lanzando la java.lang.IllegalAccessException sabrás que debo hacer para poder leer este campo o eso es imposible???
y claro que a veces es necesario conocer los campos en mi caso estoy haciendo un reporteador con hibernate y quiero que sea generico para cualquier clase que desarrollen pero bueno hasta que no te enfrentas con el problema no requieres de algo asi jeje espero respuesta gracias …
Es posible que ese código haya dejado de funcionar en versiones más recientes de la JDK (este artículo ya tiene 3 añitos de nada 🙂 ). Ten en cuenta que, en teoría, es una operación “ilegal”. Supongo que si tengo razón y no te queda otra que hacerla tendrías que bajar a nivel de memoria usando JNI o alguna cosa oscura (y aun así no tengo ni idea de si es posible, nunca he usado nada parecido).
Saludos y suerte con ello.
una disculpa lo resolvi ese mismo dia sigue funcionando en el JDK6 sin problemas y si me ayudo mucho jeje pero se me habia olvidado regresar a comentar y creo que si tendre que ver algo con JNI ahorita mi reporteador funciona excelentemente bien pero como dices es “ilegal” creo que deberia mejorarlo para que caiga en el punto de lo “legal” pero de momento ya saque la chamba jajaja gracias
Hola, estoy estudiando un ciclo superior de “Desarrollo de Aplicaciones Multiplataforma” en el cual, tenemos una asignatura de programación en JAVA. Dicha profesora de tal asignatura, no es que nos explique las cosas, y nos ha comentado que nos bajemos manuales de java para entender lo que nos manda hacer, el caso, tenéis algún manual fiable para novatos o como querríais llamarlo, que asi mismo vaya a poder entender, por que nos pone en el proyector los códigos y nos dice que los copiemos pero no explica nada. Así que me lo tengo que mirar todo por mi cuenta. Y agradecería un poco la ayuda de gente que controla este tema. Un saludo. SOK
Yo en lo personal, sin ser ningún tipo de experto en la refelxión, me ahorraría el bucle for utilizando el método que proporciona la clase java.lang.class el cual devuelve un campo a partir del nombre. (Field getDeclaredField(String fieldName))
Un cordial saludo.
Buenas Goku San.
Hace ya mucho de esto pero me suena que el método que propones devolvía null. Recuerdo que en su momento pensé que el que funcionase la solución que pongo ahí arriba debía ser algún tipo de despiste por parte de los desarrolladores de la JDK, y no me sorprendería que lo pensase por el hecho de que ambos métodos (el que yo usé y el que tú propones) devolvían resultados incoherentes. Pero habría que probarlo, porque soy incapaz de acordarme.
En cualquier caso gracias por la aportación, y si te animas a comprobarlo no dejes de contarnos el resultado :-). Saludos.
Quizá usaras el método Field getField(String fieldName) , el cual sólo devuelve los atributos que son visibles.
No obstante, lo probaré.
Un saludo.
Tenías razón y no se puede acceder al valor de un atributo si no se pone a accesible antes.
He aquí un código que lo hace de forma genérica:
public class ReflectionUtil {
private final static Logger log = Logger.getLogger(ReflectionUtil.class);
@SuppressWarnings(“unchecked”)
public static T getFieldValue(Object o, String fieldName) {
Field field = null;
try {
field = o.getClass().getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
log.error(“Impossible to obtain field ” + fieldName + ” from object ” + o, e);
return null;
}
field.setAccessible(true);
T value = null;
try {
value = (T)field.get(o);
} catch (IllegalArgumentException | IllegalAccessException e) {
log.error(“Impossible to obtain field value ” + fieldName + ” from object ” + o, e);
}
field.setAccessible(false);
return value;
}
}