Tratamiento de objetos en Java

T

Soy el típico estudiante de DAI que ha llegado a 2º sin tener ni papa de programar, más por culpa del nivel pésimo que tuvimos en 1º que por otra cosa. Así que os cuento...

En 2º nos han mandado un proyecto que consiste en una especie de zoologico, donde en una matriz se guardan los animales, cada turno realizar un movimiento aleatorio y si se cruza x especie con otra ''comestible'' se la come, por ejemplo, si hay un Guepardo() al lado de un Ñu() pues se lo come. Cada animal tiene una clase propia, con sus atributos y demás, y los animales y sus movimientos hay que realizarlos en una matriz, pero claro, yo no tengo ni idea de como guardar cada animal en la matriz ni como reconocerlos, o sea, para meterlos sería algo así.

matriz[x][y] = new Cocodrilo();

Pero luego no sé como identificar a eso que está guardado en la posicion [x][y], no se trabajar con ello, ni referenciarlo, ni moverlo a otra posicion ni nada.

Para colmo, además de los movimientos de los animales dentro de la matriz, cada x turnos un animal se reproduce, por lo que habria que crear otro objeto en la matriz y ya ni papa de como identificarlo ni nada (porque su creacion es automatica), es un caos, vaya.

Para qué creo este thread? Para que me digáis más o menos como lo haríais, o algo, estoy (la clase en general) realmente perdido... :S

Tig

para reconocerlo

if(matriz[x][y] instanceof Cocodrilo){
//muerte
}

Para moverlo, basta con reasignarlo

matriz[1][2] = matriz[3][4]
matriz[3][4] = null

No uses el clone de java, siempre acaba dando problemas.

Igual no te he solucionado nada porque si no sabes programar todo esto que he dicho te sonará a chino, pero por lo menos ya sabes reconocer si una posición contiene un cocodrilo :)

Por cierto, para programación mucho mejor www.stackoverflow.com que mediavida, pero tienen que ser preguntas sencillas, no algo tan generalista como esto.

[editado para quitar cosas erróneas]

4 respuestas
elkaoD

#2 precisamente como en Java pasas las cosas por referencia, hacer que el puntero en matriz[3][4] sea null no peta nada. Sólo estás poniendo a null el puntero a objeto. No puedes destruir el objeto derreferenciándolo. De hecho, (a no ser que me equivoque) no puedes joder ningún objeto en Java si no es derreferenciándolo de todos los punteros a objeto posibles y que el recolector de basura se encargue de destruirlo.

Otra cosa es que realice operaciones sobre el objeto (cambios en sus atributos.) Estas se realizarían en el objeto y todas sus referencias apuntarían al objeto modificado (y ahí si hay que clonarlo para tener dos copias, pero pasan a ser dos referencias distintas.) Por la descripción del problema no parece que vaya a ser el caso en ningún momento.

Si se va con esto a StackOverflow el 90% de las respuestas le pedirán que se lea un libro de Java antes de preguntar.

#1 simplemente aprende Java. Google "manual java" o lo que sea, y a empollar.

Por cierto, es triste, pero no eres el único que conozco en situaciones parecidas en DAI. Si supiéras como ha llegado más de uno a la carrera...

2 respuestas
B

#1 ibas de tarde o de mñn¿?

1 respuesta
HeXaN

#3 Si supieras cómo sale más de uno de la carrera xD

#7 Créeme lo sé xD Son profesores de ESO/Bachiller que reciben un cursillo del módulo que les toca impartir y pista.

2 respuestas
Tig

#3 es verdad, se me ha ido la cabeza. Tengo esto oxidado.

T

#4 Tarde, aunque no se a que viene la pregunta, la verdad xD

#5 Si vieras lo que saben algunos profesores del DAI...

#2 Gracias ;D

4 respuestas
B

#7

na lo pregunto xq yo el primer año empece de mñn y dure 2 meses, la gente en clase jugando viendo videos y el nivel no me gustaba.

Al año siguiente empece de tarde y la cosa cambio bastante.

dagavi

Estáis diciendo que en Java se pasa todo por referencia, cuando Java pasa todos los argumentos por valor. Otra cosa es que para objetos, no tipos simples, pase punteros, pero no es referencia.

public void f(Object d) {
    d = null;
}

Object o = new...
f(o);
// Si se pasara por referencia o aquí sería null
1 respuesta
elkaoD

#9 si usar punteros para los objetos no es referencia, ¿cómo se llama? (la pregunta es en serio)

Hasta donde yo sé, el snippet que pones en C++ tendría el mismo comportamiento, porque el puntero de referencia se pasa por valor y tú sólo cambiarías la variable local en la pila (que supongo es lo que ocurre en Java, sólo que transparente al programador.)

1 respuesta
dagavi

#10 En C++ el paso por referencia no es pasar un puntero, es indicarle que es un argumento por referencia.

El código que he puesto, haciendo un símil, sería:

void f(int*& puntero) {
    puntero = NULL;
}

int* puntero = new int; // Tiene una direccion
f(puntero);
// Aquí puntero es NULL (además de provocar un memory leak puesto que no hemos liberado)

El típico ejercicio para enseñar el paso de parámetros por valor o referencia es el swap:

void swap(int& a, int& b) {
    int c = a;
    a = b;
    b = c;
}

int a = 1, b = 2;
swap(a, b);
// a == 2 y b == 1

Es muy útil para crear estructuras de datos pues que hacer cosas como estas son válidas (y correctas, claro), evitando tener que trabajar con elementos estando situado en sus padres, o cosas por el estilo:

void insertar(Nodo*& n, int x) {
    if (n == NULL) {
        n = new Nodo(x);
    }
    else /* lo que sea*/
}

Decir que C no tiene parámetros por referencia, aunque se simulan con un <tipo>* y las llamadas pasarle la dirección con &var

1 respuesta
elkaoD

#11 *& no existe, ¿no?

Y creo (ojo, creo) que te equivocas:

void f(int& puntero) sólo significa que el argumento puntero no es un entero, sino la dirección de éste (referencia.) Cuando se llama a la función, se hace un push a la pila con la dirección del puntero, y en caso de modificar el valor de la dirección dentro de la función, sólo cambia la variable local, a no ser que hagas un puntero a un valor de dirección y modifiques su contenido con *puntero = NULL.

Voy a instalar Codeblocks y lo pruebo.

2 respuestas
dagavi

#12 *& si que existe, es pasar un puntero por referencia.

"Cuando se llama a la función, se hace un push a la pila con la dirección del puntero, y en caso de modificar el valor del puntero dentro de la función, sólo cambia la variable local. "

Tu mismo acabas de definir el paso por referencia. Java no hace eso nunca, hace una copia del valor de la variable. En el caso de ser un tipo básico habremos copiado el entero, char o lo que sea, en otro caso hemos copiado una dirección de memoria, pero no has pasado la dirección de la variable, si no su contenido.

No me lo invento, puedes buscarlo en Google xD

1 respuesta
elkaoD

#13 cómo, cómo, cómo? Sí, lo de los tipos básicos sí, pero...

"en otro caso hemos copiado una dirección de memoria, pero no has pasado la dirección de la variable, si no su contenido"

No entiendo esta frase. Si copias una dirección de memoria, pasas su dirección, ¿no? Otra cosa es que esto sea transparente para tí, pero en un paso de objetos NUNCA se pasa su contenido, sólo referencias al objeto. Si pasaras el contenido del objeto, al modificarlo desde una referencia no se modificaría desde las demás.

1 respuesta
dagavi

#12 http://ideone.com/OtGsK Aquí tienes un ejemplo de *&

Su potencial se ve con estructuras más complejas


#14 Tu mismo has definido el paso de una variable por referncia.

Si yo tengo esto:

variable x;

En la práctica el paso por referencia es hacer esto, como tu bien dices:
f(&x);

Si yo trabajo sobre "x" afecto al valor original contenido por "x" (esté en la pila o en el heap).

Eso lo ves muy claro con el tipo int, porque es un tipo básico:
int x = 10;
f(x); // f hace x = 0;
x vale 0

Sin embargo con un puntero debe de pasar lo mismo, tienes que pasar la dirección <b>al</b> puntero:
int* x = new int
f1(*x); // f1 hace x = 10;
// Aquí *x == 10
f2(x); // f2 hace detele x; x =NULL
// Aquí x == NULL

#16 Esto en Java no se puede hacer puesto que pasa todos los parámetros por valor:
Object o = new Object(); // Esto en C++ sería como Object* o = new Object
f(o); // Se copia el contenido de O al bloque de activación de la llamada de la función (y esto es la definición de paso de parámetro por valor). No se está poniendo la dirección de la variable O (definición de paso de parámetro por referencia), por lo que si haces un "o = NULL" dentro de la función fuera no pasa nada.

Edit: Me había dejado la última parte haciendo la referencia a Java ya que se me ha colgado el PC cuando estaba editando xD

1 respuesta
elkaoD

#15 pues fíjate que eso yo lo hacía siempre con & y luego con *n = new Nodo()... Me lo apunto.

1 respuesta
Khanser

#7 puedes hacer perfectamente matriz[1][2] = matriz[3][4]; matriz[3][4] = null; y no le pasaría nada a matriz[1][2]. Hacer un new cada vez que reasignas es una perdida de memoria increíble.

En java, pese a que trabajas directamente con objetos, todo son Referencias excepto los tipos primitivos que guardan sus valores en la propia referencia.

Si le pasas un objeto a un método y modificas el estado del objeto dentro del método, fuera de ésta también estará cambiado.

Para poder reconocer qué tienes en cada objeto de la matriz puedes usar la palabra reservada "instanceof".

Ejemplo

boolean isMonkey(Object o){
    return o instanceof Monkey;
}

Por cierto, lo que dice #2 de no usar el clone es una soberana soplapollez. Es normal que los clone no funcionen si no haces que tus clases implementen la interfaz Clonable porque usan el clone de la clase padre Object aunque tus clases no la hereden explícitamente.

3 respuestas
T

#17 O sea, si es una parte de la matriz estoy buscando una gacela no sería:

if(matriz[x][y]==gacela){
...
}

Sino

if(isGacela(matriz[x][y]){
...
}

(Habiendo creado la funcion isGacela, claro.

1 respuesta
Tig

#17 esto de clone lo he leído en varios lugares, entre otros a Josh Bloch http://www.artima.com/intv/bloch13.html

There are no guarantees that it preserves the invariants established by the constructors. There have been lots of bugs over the years, both in and outside Sun, stemming from the fact that if you just call super.clone repeatedly up the chain until you have cloned an object, you have a shallow copy of the object. The clone generally shares state with the object being cloned. If that state is mutable, you don't have two independent objects. If you modify one, the other changes as well. And all of a sudden, you get random behavior.

Khanser

#18 exacto, cuando comparas con "==" lo que hace es comparar referencias. Cuando quieras hacer comparaciones de igualdad entre objetos deberías usar 'equals'.
El '==' sirve cuando comparas tipos primitivos o cuando quieres saber si 2 objetos son completamente idénticos pero si por ejemplo tienes dos instancias de una clase A, que tiene un atributo "nombre" con el valor "jose", el "==" entre esas 2 instancias dará false, pese a que tu quieras compararlas por su atributo clave, por ejemplo. (Esto es solo para que veas cuando usar == y cuando usar el método 'equals')

T

Bueno, al final lo que he hecho es:

Crear en la clase de la que heredan todos los animales un toString abstracto. Luego en cada clase crear el metodo tal que así:

	public String toString() {
		return "Guepardo";
	}

Y en la clase principal para identificarlo simplemente:

if(zoo[i][j].toString().equalsIgnoreCase("Guepardo")...

Además el instanceof que puso #17 me ha venido de lujo para otra cosilla que tenía que hacer más adelante.

Bueno, muchas gracias a todos, a Hobbes también que me ayudó muchísimo ;D

2 respuestas
elkaoD

Es un poco chapu lo del toString, para eso usa getClass().getSimpleName() (o algo así era el método, te lo digo de memoria, mira la referencia.)

Si haces lo del toString al menos usa constantes.

Khanser

WARG pero para que usas el equals guepardo ahi #21 ? Ahí también puedes usar el instanceof. De hecho es lo mejor...
Si al final te decides por el equals si o si, ponlo al reves. "Guepardo".equalsIgnoreCase( X ) te evita de nullpointers :)

B

#5: Hombre, dudo que nadie apruebe un PFC sin saber cosas tan básicas.

Aunque también dudo que nadie apruebe DAI sin saber cosas tan básicas. Pero recordemos que #1 aún es alumno y está aprendiendo.

Antes de tomar las soluciones que te den por ahí (que yo diría que la primera está bien, aunque hace tiempo que no me peleo con este tipo de problemas en Java), te recomiendo que leas sobre polimorfismo y el tratado de objetos.

El polimorfismo tiene que ver con la relación de similitud entre las superclases y las subclases (por ejemplo un Gato siempre es un Animal, al igual que un Perro, puedes tratarlos como animales a los dos pero si quieres saber qué tipo exacto son necesitarás instance of).

En cuanto al tratamiento de objetos, sí que se pasan por valor, lo que pasa es que a efectos prácticos es por referencia (de todas formas esto de momento no debería de importarte).

En cualquier caso mírate esos dos puntos en algún manual que soy pésimo explicando.

7 días después
Rubo18

#21 Ufh si hubiese visto esto antes te habría contestado yo xD. Si me dices por donde vas ahora mismo a lo mejor te puedo echar un cable, que a mi sólo me queda acabar la GUI.

Y, por cierto,
#7 "#5 Si vieras lo que saben algunos profesores del DAI..."

Doy fe absoluta de eso XD.

T

java pasa todos los atributos por valor. Lo que pasa es que cuando pasas un objeto que no es basico pasa una copia de la direccion del objeto. Si dentro de la funcion modificas un atributo del objeto el objeto exterior se modifica ya que lo que cambias es la posicion de memoria a la que estan apuntando los dos objetos. En cambio si modificas el objeti en si este no cambia fuera de la funcion.

La manera mas sencilla de ver esto es crear una clase con dos enteros (a, b).
Entonces hacemos las dos funciones siguientes:

fun1(obj o){
o.a = 2;
o.b=2;
}

fun2(obj o){
objeto o2 = new objeto();
o2.a=3;
o2.b=3;

o = o2;
}

si declaramos un objeto o con a=1 y b=1 y lo pasamos a la funcion fun2 tendrems a la salida que a y b de este valor han sido modificados y valen 2.
En cambio si lo pasamos a la funcion 2 el objeto no se modifica.
Esto es debido a que se pasa una copia de la direccion del objeto y no la direccion del objeto.

P.S.: odio escribir desde un mvl

1 respuesta
elkaoD

#26 Lo que pasa es que cuando pasas un objeto que no es basico pasa una copia de la direccion del objeto
Vamos, lo que viene a ser paso por referencia.

1 respuesta
T

#27 Falso, Java no pasa los objetos por referencia. Java pasa una copia de la variable (tanto para tipos básicos como para objetos). En Java cuando se trata de un objeto la variable contiene la dirección a la que apunta, es decir, no contiene el objeto en si, sino la dirección donde esta el objeto. Se trata de un puntero implicito.

Cuando pasas una variable por referecia a una función y dentro de esta función haces que la variable apunte a otro objeto al salir la variable apuntará al nuevo objeto que se le ha asignado. En Java no pasa eso y la variable sigue apuntando al objeto que tenía antes. Esto es debido a que se pasa una copia de la variable.
Lo que hace que mucha gente se confunda es que esa copia apunta a la misma dirección que la original por lo que si modifico el contenido de la copia estoy modificando el contenido de la original. En cambio si hago que la variable apunte a un nuevo objeto al salir no lo habré modificado.

En realidad lo que hace Java es algo así:
obj o1, o2;
o1.a =1;
o2=o1;
o2.a=2;
Aquí tendremos que o1.a también vale 2 ya que o2.a y o1.a apuntan a la misma dirección de memoria.

Si ahora hago:
obj o3;
o2=o3;
o3.a=5
Ahora tenemos que o2.a y o3.a valen 5 pero o1.a sigue valiendo 2.

Esto es exactamente lo que pasa con el paso de parámetros en Java. Si remplazaces o2=o1; por una llamada a una función pasandole o1, cuya entrada sea un objeto o2 y el código las instrucciones que siguen, tendrías exactamente el mismo resultado.

Es decir
main(){
obj o1;
o1.a=1;
f(o1);
}

f(obj o2){
Aquí tenemos que o2 apunta a la misma posición que o1;
o2.a = 3;
*
obj o3;
o2=o3;
**
o2.a=5;


}

*En este punto como o2 apuntaba a la misma dirección que o1 o2.a=o1.a = 3.
** En este punto de ejecución si fuese por referencia o1, o2 y o3 apuntarían a la misma dirección de memoria pero en Java no es así. o1 sigue apuntando al objeto que tenía antes de llamar a f y o2 y o3 apuntan al mismo objeto.
*** En este punto si el pasaje fuera por referencia o2.a = o2.a = o3.a = 5 pero como Java pasa una copia de la dirección de o1 tenemos que o1.a= 3 y o2.a = o3.a = 5.
Espero que quede más claro que ante.

Perdón para contaminar el hilo original.

3 respuestas
Khanser

#28 que coño hablas.

Java a las funciones envía la dirección de memoria del objeto, en caso de tipos primitivos el valor está implicito en su valor del 'puntero'.

No se en qué centro has estudiado pero eso es un paso por referencia.

Otra cosa es que en C puedas pasar punteros a referencias de objetos, que si apuntas a otro sitio, si que varía, pero pork el contenido del puntero del parámetro es en sí la referencia a otra variable.

PD: Te recomiendo la lectura del libro SCJP 6 página 191

1 2 respuestas
elkaoD

#28 pero eso no es paso por referencia?

#29 thx, me llevo volviendo loco desde #2. No entendía qué tenía Java que no fuera por referencia.

1 respuesta