¿Puedo pasar parámetros por referencia en Java?

Respuestas:

225

Java es confuso porque todo se pasa por valor . Sin embargo, para un parámetro de tipo de referencia (es decir, no un parámetro de tipo primitivo), es la referencia misma la que se pasa por valor, por lo tanto, parece ser una referencia de paso (y las personas a menudo afirman que sí lo es). Este no es el caso, como se muestra a continuación:

Object o = "Hello";
mutate(o)
System.out.println(o);

private void mutate(Object o) { o = "Goodbye"; } //NOT THE SAME o!

Se imprimirá Helloen la consola. Las opciones si desea que se imprima el código anterior Goodbyeson utilizar una referencia explícita de la siguiente manera:

AtomicReference<Object> ref = new AtomicReference<Object>("Hello");
mutate(ref);
System.out.println(ref.get()); //Goodbye!

private void mutate(AtomicReference<Object> ref) { ref.set("Goodbye"); }
oxbow_lakes
fuente
48
Además, se puede usar una matriz de longitud 1 para crear una referencia si realmente quieres confundir a las personas :)
Christoffer
2
AtomicReference y las clases correspondientes (AtomicInteger) son las mejores para tales operaciones
Naveen
3
AtomicReference es una exageración, no necesariamente desea barrera de memoria allí, y pueden costarle. JRuby tuvo un problema de rendimiento debido a una variable volátil utilizada al construir un Objeto. La opción de usar array como referencia ad-hoc me parece bastante buena, y he visto que se usa más de una vez.
Elazar Leibovich
No creo que esto sea confuso, sin embargo, este es un comportamiento normal. Los tipos de valor existen en la pila y los tipos de referencia existen en el montón, pero la referencia a los tipos de referencia también existe en la pila. Por lo tanto, siempre opera con un valor que está en la pila. Creo que la pregunta aquí fue: ¿es posible pasar referencias de la pila que apuntan a algún otro valor que también esté en la pila? ¿O pasar la referencia de otra referencia que señala algún valor que vive en el montón?
eomeroff
Esta es una gran información. Había estado usando matrices, pero esto parece algo creado para el trabajo. Sin embargo, no sé sobre el rendimiento de este vs arrays, si en un ciclo crítico de rendimiento
steveh
65

¿Puedo pasar parámetros por referencia en Java?

No.

Por qué ? Java solo tiene un modo de pasar argumentos a los métodos: por valor.

Nota:

Para los primitivos esto es fácil de entender: obtienes una copia del valor.

Para todos los demás, obtienes una copia de la referencia y esto se llama también pasar por valor.

Todo está en esta imagen:

ingrese la descripción de la imagen aquí

PeterMmm
fuente
25

En Java no hay nada a nivel de lenguaje similar a la ref . En Java solo hay paso por valor semántico

En aras de la curiosidad, puede implementar una semántica de referencia en Java simplemente envolviendo sus objetos en una clase mutable:

public class Ref<T> {

    private T value;

    public Ref(T value) {
        this.value = value;
    }

    public T get() {
        return value;
    }

    public void set(T anotherValue) {
        value = anotherValue;
    }

    @Override
    public String toString() {
        return value.toString();
    }

    @Override
    public boolean equals(Object obj) {
        return value.equals(obj);
    }

    @Override
    public int hashCode() {
        return value.hashCode();
    }
}

caso de prueba:

public void changeRef(Ref<String> ref) {
    ref.set("bbb");
}

// ...
Ref<String> ref = new Ref<String>("aaa");
changeRef(ref);
System.out.println(ref); // prints "bbb"
dfa
fuente
¿Por qué no usar java.lang.ref.Reference en su lugar?
Codificado el
java.lang.ref.Reference no tiene un método establecido, no es mutable
dfa
¿Por qué no usar AtomicReference(para no primitivos)? O AtomicSomething(por ejemplo AtomicBoolean:) para primitivas?
Arvin el
Desafortunadamente, <T> no acepta tipos primitivos. Me gustaría hacer esto: Ref <int>
jk7
1
@ jk7 ¿Es realmente lo suficientemente importante como para tener que usar en intlugar de Integer, en boollugar de Booleany así sucesivamente? Es decir, ¿las clases de envoltura (que se desenvuelven automáticamente si le preocupa la sintaxis) no funcionan?
Paul Stelian
18

De James Gosling en "El lenguaje de programación Java":

"... Hay exactamente un modo de paso de parámetros en Java, pasar por valor, y eso simplifica las cosas ..."

duffymo
fuente
2
El pronunciamiento del dios del lenguaje debe declararse la respuesta.
ingyhere
3
@ingyhere: The pronouncement of the language god should be declared the answerNo realmente. A pesar de lo que el creador de Java piensa o cree, la respuesta absoluta vendría de una cita de la especificación del lenguaje.
paercebal
1
De hecho, está en el JLS, en la sección 8.4.1 , y el JLS cita a Gosling como primer autor: "Cuando se invoca el método o el constructor (§15.12), los valores de las expresiones de argumentos reales inicializan las variables de parámetros recién creadas , cada uno del tipo declarado, antes de la ejecución del cuerpo del método o constructor ".
ingyhere
Exactamente. El sarcasmo de los miembros de baja reputación no es útil.
duffymo
11

No creo que puedas. Su mejor opción podría ser encapsular lo que desea pasar "por referencia" a otra instancia de clase y pasar la referencia de la clase (externa) (por valor). Si te das cuenta de lo que quiero decir...

es decir, su método cambia el estado interno del objeto que se pasa, que luego es visible para la persona que llama.

Marc Gravell
fuente
7

Java siempre se pasa por valor.

Cuando pasa un primitivo, es una copia del valor, cuando pasa un objeto, es una copia del puntero de referencia.

Nick Holt
fuente
4

Otra opción es usar una matriz, por ejemplo, void method(SomeClass[] v) { v[0] = ...; } pero 1) la matriz debe inicializarse antes de invocar el método, 2) aún no se puede implementar, por ejemplo, el método de intercambio de esta manera ... De esta manera se usa en JDK, por ejemplo, en java.util.concurrent.atomic.AtomicMarkableReference.get(boolean[]).

Michal Misiak
fuente