2009-04-07 20 views

Respuesta

15

Hace mucho tiempo tuve una conversación con Ken Arnold (miembro de tiempo del equipo de Java), esto habría sido en la primera conferencia de Java One, probablemente en 1996. Dijo que estaban pensando en agregando múltiples valores devueltos para que pueda escribir algo como:

x, y = foo(); 

The rec La forma omitida de hacerlo en ese entonces, y ahora, es crear una clase que tenga múltiples miembros de datos y devolverlos en su lugar.

Basado en eso, y otros comentarios hechos por personas que trabajaron en Java, yo diría que la intención es/era que devuelva una instancia de una clase en lugar de modificar los argumentos que se aprobaron en.

Este es una práctica común (como lo es el deseo de los programadores C de modificar los argumentos ... eventualmente ven la forma Java de hacerlo normalmente. Solo piensen en ello como devolver una estructura. :-)

(Edición basada en siguiente comentario)

Estoy leyendo un archivo y generando dos matrices, de tipo String e int de , escogiendo un elemento para ambos desde cada línea. Quiero devolver ambos a cualquier función que lo llame que un archivo dividir de esta manera.

creo que, si te estoy entendiendo correctamente, THT que probablemente hacer soemthing así:

// could go with the Pair idea from another post, but I personally don't like that way 
class Line 
{ 
    // would use appropriate names 
    private final int intVal; 
    private final String stringVal; 

    public Line(final int iVal, final String sVal) 
    { 
     intVal = iVal; 
     stringVal = sVal; 
    } 

    public int getIntVal() 
    { 
     return (intVal); 
    } 

    public String getStringVal() 
    { 
     return (stringVal); 
    } 

    // equals/hashCode/etc... as appropriate 
} 

y luego tener su método como este:

public void foo(final File file, final List<Line> lines) 
{ 
    // add to the List. 
} 

y luego llamar es así:

{ 
    final List<Line> lines; 

    lines = new ArrayList<Line>(); 
    foo(file, lines); 
} 
+1

Sé exactamente lo que estás diciendo, pero la construcción de objetos con el único propósito de devolver múltiples valores ha demostrado ser confusa en la práctica, sin duda para mí. Definitivamente va en contra de lo que se supone que es una clase. – euphoria83

+0

No está en contra de lo que debería ser una clase. Hay cosas tales como "clases de valores" en las que el objetivo principal es simplemente mantener el estado y no proporcionar ningún comportamiento. ¿Cuál es la confusión que has experimentado? – TofuBeer

+0

Solo que entonces hay un par de clases que no hacen nada excepto sentarse ahí para la transferencia de valor. Intento hacer una clase solo si tiene/tiene algo significativo. – euphoria83

2

No puede devolver muchos valores, pero puede pasar objetos a un método y hacer que el método mute esos valores. Eso es perfectamente legal. Tenga en cuenta que no puede pasar un objeto y hacer que el objeto se convierta en un objeto diferente. Es decir:

private void myFunc(Object a) { 
    a = new Object(); 
} 

dará lugar a temporalmente y localmente cambiando el valor de a, pero esto no va a cambiar el valor de la persona que llama, por ejemplo, de:

Object test = new Object(); 
myFunc(test); 

Después myFunc vuelve, tendrá el Objeto viejo y no el nuevo.

legal (ya menudo desanimado) es algo como esto:

private void changeDate(final Date date) { 
    date.setTime(1234567890L); 
} 

Escogí Date por una razón. Esta es una clase que la gente acepta ampliamente que nunca debería haber sido mutable. El método anterior cambiará el valor interno de cualquier objeto Date que le pase. Este tipo de código es legal cuando está muy claro que el método va a mutar o configurar o modificar lo que se aprobó en

NOTA:. En general, se dice que un método debe hacer una de estas cosas:

  • retorno void y mutar sus objetos de entrada (como Collections.sort()), o
  • Volver algún cálculo y no mutar objetos entrantes en absoluto (como Collections.min()), o
  • devolver una "vista" del objeto entrante, pero no lo hacen modificar el objeto entrante (como Collections.checkedList() o Collections.singleton())
  • Mutee un objeto entrante y devuélvalo (Collections no tiene un ejemplo, pero StringBuilder.append() es un buen ejemplo).

Los métodos que mutar objetos entrantes y devolver un valor de retorno separado a menudo haciendo demasiadas cosas.

+0

Esa es precisamente mi pregunta. Al igual que con Date, ¿no deberíamos mutar cualquier objeto pasado por referencia? – euphoria83

+0

Collections.sort() es un uso válido, si se requiere que uno no afecte al original, pase la copia. De lo contrario, es bastante claro que la clasificación del método jugaría con la lista aprobada. –

+3

Los objetos nunca se pasan por referencia; objeto * referencias * se pasan por valor. Pero de eso no se trata este hilo. –

5

Ver este RFE lanzado en 1999:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4222792

No creo que la intención era permitir que cada vez que en el lenguaje Java, si es necesario devolver múltiples valores que necesita para encapsular en un objeto.

utilizando lenguajes como Scala sin embargo se puede devolver tuplas, consulte:

http://www.artima.com/scalazine/articles/steps.html

También se pueden utilizar medicamentos genéricos en Java para devolver un par de objetos, pero eso es todo lo que yo sepa.

EDIT: Tuplas

Sólo para añadir un poco más en esto. Anteriormente implementé un par en proyectos debido a la falta dentro del JDK. Enlace a mi aplicación está aquí:

http://pbin.oogly.co.uk/listings/viewlistingdetail/5003504425055b47d857490ff73ab9

Nota, no hay un código hash o igual a este, lo que probablemente se debe añadir.

también me encontré con esto mientras haciendo algunas investigaciones en esta pregunta que proporciona funcionalidad tupla:

http://javatuple.com/

Se le permite crear Par incluyendo otros tipos de tuplas.

+0

Lisp lo permite también. Sus soluciones son correctas y obvias, excepto por el uso de Genéricos. ¿Puedes explicar eso en breve ya que este uso es nuevo para mí? – euphoria83

+0

Agregué el enlace de implementación de par y un enlace a javatuple, que se ve bastante bien. Espero que ayude. – Jon

+0

Hola gracias. Esto es genial. – euphoria83

0

Ciertamente hay métodos que modifican un objeto pasado como parámetro (see java.io.Reader.read(byte[] buffer) como ejemplo, pero no he visto los parámetros utilizados como una alternativa para un valor de retorno, especialmente con parámetros múltiples. Puede funcionar técnicamente, pero es no estándar.

+0

Definitivamente funcionará. :) – euphoria83

0

En general no se considera terriblemente buenas prácticas, pero hay casos muy ocasionales en el JDK donde se hace esto. Mire el parámetro 'biasRet' de View.getNextVisualPositionFrom() y métodos relacionados, por ejemplo: en realidad es una matriz unidimensional que se llena con un "valor de retorno extra".

¿Por qué hacer esto? Bueno, solo para evitar tener que crear una definición de clase adicional para el "valor extra de retorno ocasional". Es desordenado, poco elegante, de mal diseño, no orientado a objetos, bla, bla. Y todos lo hemos hecho de vez en cuando ...

+0

Tiene sentido para las matrices. Al menos me hace esto en C y C++. – euphoria83

5

Ojalá hubiera una clase Pair<E,F> en JDK, principalmente por este motivo. Hay Map<K,V>.Entry, pero crear una instancia siempre fue un gran problema.

Ahora uso com.google.common.collect.Maps.immutableEntry cuando necesito un Pair

+0

en realidad, Google está haciendo un gran trabajo para llenar los huecos en la implementación de Java de Sun. Estos no necesitan ser errores. Pero definitivamente fueron necesarios para una mejor piratería. – euphoria83

7

En mi opinión, si estamos hablando de un método público, se debe crear una clase separada que representa un valor de retorno. Cuando se tiene una clase separada:

  • que sirve como una abstracción (es decir, un Point clase en lugar de la matriz de dos largos)
  • cada campo tiene un nombre
  • se pueden hacer inmutable
  • hace que la evolución de API mucho más fácil (es decir, ¿qué pasa con devolver 3 en lugar de 2 valores, cambiar el tipo de campo, etc.)

Siempre optaría por devolver una nueva instancia, en lugar de modificar un valor pa ssed in. Me parece mucho más claro y favorece la inmutabilidad.

Por otro lado, si se trata de un método interno, supongo que cualquiera de los siguientes podrían utilizarse:

  • una matriz (new Object[] { "str", longValue })
  • una lista (Arrays.asList(...) rendimientos lista inmutable)
  • pair/clase tupla, como this
  • clase interna estática, con campos públicos

Aún así, preferiría la última opción, equipada con un constructor adecuado. Eso es especialmente cierto si te encuentras devolviendo la misma tupla desde más de un lugar.

0

Generalmente lo que dijo Eddie, pero me gustaría añadir una más:

  • Mutate uno de los objetos de entrada y devuelven un código de estado. En general, esto solo se debe usar para argumentos que son buffers explícitos, como Reader.read (char [] cbuf).
0

Tenía un objeto Result que se conecta en cascada a través de una serie de métodos de validación como un parámetro de método. Cada uno de estos métodos vacíos de validación mutaría el objeto de parámetro de resultado para agregar el resultado de la validación.

Pero esto es imposible de probar porque ahora no puedo resguardar el método void para devolver un valor stub para la validación en el objeto Result.

Por lo tanto, desde una perspectiva de prueba, parece que uno debería preferir devolver un objeto en lugar de mutar un parámetro de método.

Cuestiones relacionadas