2010-02-05 18 views
25
package myintergertest; 

/** 
* 
* @author Engineering 
*/ 
public class Main { 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     //this one does not increment 
     Integer n = new Integer(0); 
     System.out.println("n=" + n); 
     Increment(n); 
     System.out.println("n=" + n); 
     Increment(n); 
     System.out.println("n=" + n); 
     Increment(n); 
     System.out.println("n=" + n); 
     Increment(n); 

     //this one will increment 
     MyIntegerObj myInt = new MyIntegerObj(1); 
     Increment(myInt); 
     System.out.println("myint = " + myInt.get()); 
     Increment(myInt); 
     System.out.println("myint = " + myInt.get()); 
     Increment(myInt); 
     System.out.println("myint = " + myInt.get()); 

    } 

    public static void Increment(Integer n) { 
     //NO. this doesn't work because a new reference is being assigned 
     //and references are passed by value in java 
     n++; 
    } 

    public static void Increment(MyIntegerObj n) { 
     //this works because we're still operating on the same object 
     //no new reference was assigned to n here. 
     n.plusplus(); //I didn't know how to implement a ++ operator... 
    } 
} 

El resultado para todos es n = 0. El entero n es un objeto y, por lo tanto, se pasa por referencia, entonces, ¿por qué no se refleja el incremento en el método del llamante (principal)? Esperaba que la salida fuera n = 0 n = 1 n = 2 etc ...Cómo incrementar una clase El entero hace referencia al valor en Java desde otro método

ACTUALIZACIÓN: Aviso Actualicé el ejemplo de código anterior. Si estoy entendiendo correctamente, Jon Skeet respondió la pregunta de por qué miInt aumentaría y por qué n no. Es porque n obtiene una nueva referencia asignada en el método Increment. Pero myInt NO recibe una nueva referencia asignada, ya que llama a una función miembro.

¿Eso suena como que entiendo correctamente lol?

Respuesta

36

No, los objetos no se pasan por referencia. Las referencias se pasan por valor; hay una gran diferencia. Integer es un tipo inmutable, por lo tanto, no puede cambiar el valor dentro del método.

Su declaración n++; es efectivamente

n = Integer.valueOf(n.intValue() + 1); 

lo tanto, que asigna un valor diferente a la variable n en Increment - sino como Java solamente tiene el paso por valor, que no afecta el valor de n en el método de llamada.

EDITAR: Para responder a su actualización: eso es correcto. Presumiblemente, su tipo "MyIntegerObj" es mutable y cambia su estado interno cuando llama al plusplus(). Ah, y no te molestes en buscar cómo implementar un operador: Java no admite operadores definidos por el usuario.

+0

¿Querías decir '** Objects ** ** pasado por valor - ...'? – FrustratedWithFormsDesigner

+8

No, no lo hizo.Los primitivos y las referencias se pasan por valor. Los objetos no se pasan en absoluto; ellos viven en el montón. – danben

+0

Creo que entiendo, vea mis actualizaciones y hágamelo saber. ¡¡¡Gracias!!! – cchampion

6

Está buscando algo similar a la referencia de C++ o al parámetro C# 'out. Java (y muchos otros idiomas) no es compatible con esto.

Este tipo de comportamiento se logra típicamente cambiando el método de vacío que (en este caso) int: public static int increment(int n) { return ++n; }

Si el método ya está volviendo algo, entonces se puede crear una nueva clase que tiene dos campos (puede incluso ser público): el valor de retorno original y el nuevo valor de n.

Alternativamente, también puede envolver el número entero dentro de una clase Cell genérica. Sólo tiene que escribir esta clase celular una vez:

public class Cell<T> { 
    public Cell(T t) { value = t; } 
    public void set(T t) { value = t; } 
    public T get() { return value; } 
} 

entonces Se puede utilizar en todas las situaciones donde se desea un método para cambiar una primitiva:

public static void increment(Cell<Integer> n) { 
    n.set(n.get()++); 
} 

Cell<Integer> n = new Cell<Integer>(0); 
increment(n); 
increment(n); 
increment(n); 
System.out.println(n.get()); // Output: 3 
22

Se podría utilizar un AtomicInteger de java.util.concurrent.atomic . Tiene un método 'incrementAndGet' que es adecuado para su escaparate.

+0

¿Qué? Sí, quiero decir que funciona, pero no es realmente una buena idea. Hará cosas como sincronizar los búferes de sus procesadores, ya que de lo contrario no es una operación atómica. Realmente, no deberías usar una versión atómica de un tipo de datos a menos que necesites esa atomicidad ... – Jasper

+0

@Jasper, no puedo discutir eso. Es solo otra herramienta en mi caja de herramientas. Elijo dependiendo de la tarea en cuestión. Y a menos que el generador de perfiles señale un problema en ese punto en particular, sigue siendo una opción. – DerMike

1

es un value object que significa que el valor no puede cambiar.

Tenga en cuenta que n++ es el equivalente a n = n + 1. Solo está cambiando el valor al que hace referencia la variable local n, no el valor de n.

2

No puede. Java es estrictamente pass-by-value.

Ver http://javadude.com/articles/passbyvalue.htm

opciones son para envolver el número entero en un objeto (o matriz), que pasan dentro y actualización, devolver un valor, o hacer el entero una variable de clase/instancia.

Cuál es mejor depende de la situación.

3

Como mencionó Jon Skeet, todos los métodos en Java son pass-by-value, no pass-by-reference. Como los tipos de referencia se pasan por valor, lo que tiene dentro del cuerpo de la función es una copia de la referencia; esta copia y la referencia original apuntan al mismo valor.

Sin embargo, si reasigna la copia dentro del cuerpo de la función, no tendrá ningún efecto en el resto de su programa, ya que esa copia saldrá del alcance cuando la función finalice.

Pero considere que realmente no necesita pasar por referencia para hacer lo que desea hacer. Por ejemplo, no necesita un método para incrementar un Integer - puede simplemente incrementarlo en el punto en su código donde necesita incrementarse.

Para tipos más complejos, como establecer alguna propiedad en un objeto, es probable que el objeto con el que está trabajando sea mutable; en ese caso, una copia de la referencia funciona bien, ya que la desreferencia automática lo llevará al objeto original cuyo setter está llamando.

3

Nota al margen: En mi humilde opinión, escribir n ++ en un objeto Integer es extremadamente engañoso. Esto se basa en una característica de Java llamada "autoboxing" en la que convierte las primitivas en sus correspondientes objetos de boxeo, como int a Integer o boolean a boolean, de forma automática y sin previo aviso. Francamente, creo que es una mala característica. Sí, a veces le simplifica la vida al hacer automáticamente una conversión práctica para usted, pero también puede hacer conversiones que no tenía la intención y que no sabe que están sucediendo, con efectos secundarios no deseados.

De todos modos, hay dos maneras de lograr lo que está tratando de hacer parecer:

One: tienen la función de incremento regresar un nuevo entero con el valor actualizado. Al igual que:

public Integer increment(Integer n) 
    { 
     Integer nPlus=Integer.valueOf(n.intValue()+1); 
    } 

entonces lo llaman con:

Integer n=Integer.valueOf(0); 
n=increment(n); // now n==1 
n=increment(n); // now n==2 

Etc.

dos: crear su propia clase que se asemeja entero pero que es mutable. Me gusta:

class MutableInteger 
{ 
    int value; 
    public MutableInteger(int n) 
    { 
    value=n; 
    } 
    public void increment() 
    { 
    ++value; 
    } 
    ... whatever other functions you need ... 
} 

Por supuesto, la gran dificultad para esto es que prácticamente tiene que reinventar la clase Integer.

2

Como DerMike mencionado se puede lograr esto de la siguiente manera:

public void doStuff() { 
    AtomicInteger i = new AtomicInteger(0); 
    increment(i); 
    System.out.println(i); 
} 

    public void increment(AtomicInteger i) { 
    i.set(i.get() + 1); 
} 
Cuestiones relacionadas