2010-06-27 13 views
11

El compilador de Scala a menudo puede inferir tipos de devolución para los métodos, pero hay algunas circunstancias en las que se requiere especificar el tipo de devolución. Los métodos recursivos, por ejemplo, requieren que se especifique un tipo de devolución.¿Cuándo se requiere un tipo de devolución para los métodos en Scala?

Veo que a veces recibo el mensaje de error "método sobrecargado (methodname) requiere tipo de devolución", pero no es una regla general que los tipos de devolución siempre se especifiquen para métodos sobrecargados (tengo ejemplos donde no obtengo este error).

¿Cuándo se requiere exactamente especificar un tipo de devolución, para los métodos en general y específicamente para los métodos sobrecargados?

+4

Como cuestión de (mi estilo personal), que dan tipos de retorno explícitas para todos, pero los métodos más simples (básicamente, de una sola línea sin lógica condicional). Tenga en cuenta que si permite que el compilador infiera el tipo de resultado de un método, puede ser más específico de lo que desee. (Por ejemplo, 'HashMap' en lugar de' Map'.) –

+0

@Randall sí, buen punto (sobre el tipo de devolución es demasiado específico). – Jesper

Respuesta

18

El Chapter 2. Type Less, Do More del libro Programming Scala menciones:

Cuando anotaciones de tipos explícitos son obligatorios.

En términos prácticos, usted tiene que proporcionar anotaciones de tipo explícitas para las siguientes situaciones:

valores de retorno del método en los siguientes casos:

  • Cuando se pide explícitamente el retorno de un método (incluso a las el fin).
  • Cuando un método es recursivo.
  • Cuando un método está sobrecargado y uno de los métodos llama a otro. El método de llamada necesita una anotación de tipo de devolución.
  • Cuando el tipo de devolución inferida sería más general de lo que pretendía, por ejemplo, Any.

Ejemplo:

// code-examples/TypeLessDoMore/method-nested-return-script.scala 
// ERROR: Won't compile until you put a String return type on upCase. 

def upCase(s: String) = { 
    if (s.length == 0) 
    return s // ERROR - forces return type of upCase to be declared. 
    else 
    s.toUpperCase() 
} 

Los métodos sobrecargados a veces puede requerir un tipo de retorno explícito. Cuando uno de estos métodos llama a otro, debemos agregar un tipo de devolución al que hace la llamada, como en este ejemplo.

// code-examples/TypeLessDoMore/method-overloaded-return-script.scala 
// Version 1 of "StringUtil" (with a compilation error). 
// ERROR: Won't compile: needs a String return type on the second "joiner". 

object StringUtil { 
    def joiner(strings: List[String], separator: String): String = 
    strings.mkString(separator) 

    def joiner(strings: List[String]) = joiner(strings, " ") // ERROR 
} 
import StringUtil._ // Import the joiner methods. 

println(joiner(List("Programming", "Scala"))) 

Los dos joiner métodos concatenar una List de cadenas juntos.
El primer método también toma un argumento para la cadena de separación.
El segundo método llama al primero con un separador "predeterminado" de un solo espacio.

Si ejecuta este script, obtendrá el siguiente error.

... 9: error: overloaded method joiner needs result type 
def joiner(strings: List[String]) = joiner(strings, "") 

Desde el segundo método joiner llama a la primera, se requiere una explícita tipo String retorno.Se debe tener este aspecto:

def joiner(strings: List[String]): String = joiner(strings, " ") 

Básicamente, especificando el tipo de retorno puede ser una buena práctica a pesar de Scala puede inferir que.


Randall Schulz comentarios:

Como cuestión de (mi personal) estilo, me dan tipos de retorno explícitas para todos, pero los métodos más simples (básicamente, de una sola línea sin lógica condicional).

Tenga en cuenta que si permite que el compilador infiera el tipo de resultado de un método, puede ser más específico de lo que desee. (Por ejemplo, HashMap en lugar de mapa.)

Y puesto que es posible que desee exponer la interfaz mínima en su tipo de retorno (véase, por ejemplo, este SO question), este tipo de inferencia podría ponerse en el camino.


Y sobre el último escenario ("Cuando el tipo de retorno inferido sería más general de lo previsto"), Ken Bloom añade:

especifique el tipo de retorno cuando se quiere que el compilador para verificar ese código en la función devuelve el tipo que espera

(el código defectuoso que provoca una "más general que el tipo de rendimiento esperado fue:

// code-examples/TypeLessDoMore/method-broad-inference-return-script.scala 
// ERROR: Won't compile. Method actually returns List[Any], which is too "broad". 

def makeList(strings: String*) = { 
    if (strings.length == 0) 
    List(0) // #1 
    else 
    strings.toList 
} 

val list: List[String] = makeList() // ERROR 

, que erróneamente interpretado y Lista [Cualquier] debido a devolver una lista vacía, pero Ken llamó a cabo:

List(0) no crea una lista con 0 elementos.
Crea un List[Int] que contiene un elemento (el valor 0).
Por lo tanto, un List[Int] en una rama condicional y un List[String] en la otra rama condicional se generalizan a List[Any].
En este caso, el tipeo no es excesivamente general, es un error en el código.
)

+0

"Cuando el tipo de devolución inferida sería más general de lo que pretendía, por ejemplo, Cualquiera". Esto es extraño. En realidad, consideraría un error en el Typer. Nunca he experimentado algo así. ¿Tienes un ejemplo? – soc

+0

@soc: http://programming-scala.labs.oreilly.com/ch02.html tiene un ejemplo donde devolverá 'List (0)' ('List' of size 0) y' List [Any] 'en lugar de' List [String] 'a menos que especifique el tipo de devolución. – VonC

+2

'List (0)' no crea una lista con 0 elementos. Crea una 'Lista [Int]' que contiene un elemento (el valor '0'). Así, una 'Lista [Int]' en una rama condicional y una 'Lista [Serie]' en la otra rama condicional se generalizan a 'Lista [Cualquiera]'. En este caso, el typer no es excesivamente general, es un error en el código. Lo cual agrega otra regla para especificar los tipos de devolución: ** especifique el tipo de devolución cuando desee que el compilador verifique que el código en la función devuelve el tipo que esperaba. ** –

Cuestiones relacionadas