O Scala não possui sistemas seguros enumcomo o Java. Dado um conjunto de constantes relacionadas, qual seria a melhor maneira em Scala de representar essas constantes?
Sério, o aplicativo não deve ser usado. NÃO foi consertado; foi introduzida uma nova classe, App, que não apresenta os problemas mencionados por Schildmeijer. O mesmo com "object foo extends App {...}" E você tem acesso imediato aos argumentos da linha de comando através da variável args.
AmigoNico 25/07/12
scala.Enumeration (que é o que você está usando no seu exemplo de código "objeto WeekDay" acima) não oferece correspondência exaustiva de padrões. Pesquisei todos os diferentes padrões de enumeração atualmente em uso no Scala e forneço uma visão geral deles nesta resposta do StackOverflow (incluindo um novo padrão que oferece o melhor do scala.Enumeration e o padrão "traço selado + objeto de caso": stackoverflow. com / a / 25923651/501113
chaotic3quilibrium
377
Devo dizer que o exemplo copiado da documentação do Scala por skaffman acima é de utilidade limitada na prática (você também pode usar case objects).
Para obter algo parecido com um Java Enum(ou seja, com métodos toStringe valueOfmétodos sensíveis - talvez você esteja mantendo os valores de enum em um banco de dados), é necessário modificá-lo um pouco. Se você usou o código do skaffman :
valueOfA substituição de @macias é withName, que não retorna uma opção e gera um NSE se não houver correspondência. O que!
Bluu 31/01
6
@Bluu Você pode adicionar valueOf você mesmo: def valueOf (name: String) = WeekDay.values.find (_. ToString == name) para ter uma opção
centr
@centr Quando tento criar um Map[Weekday.Weekday, Long]e adicionar um valor, digamos Monque o compilador lança um erro de tipo inválido. Weekday.Weekday esperado Valor encontrado? Por que isso acontece?
Sohaib 14/06
@Shahaib Deve ser o mapa [Weekday.Value, Long].
centr 16/06
99
Existem muitas maneiras de fazer.
1) Use símbolos. No entanto, ele não fornecerá nenhum tipo de segurança, além de não aceitar não-símbolos, onde um símbolo é esperado. Só estou mencionando aqui por exaustividade. Aqui está um exemplo de uso:
def update(what:Symbol, where:Int, newValue:Array[Int]):MatrixInt=
what match{case'row=> replaceRow(where, newValue)case'col|'column=> replaceCol(where, newValue)case _ =>thrownewIllegalArgumentException}// At REPL:
scala>val a = unitMatrixInt(3)
a: teste7.MatrixInt=/100\|010|\001/
scala> a('row,1)= a.row(0)
res41: teste7.MatrixInt=/100\|100|\001/
scala> a('column,2)= a.row(0)
res42: teste7.MatrixInt=/101\|010|\000/
def update(what:Dimension, where:Int, newValue:Array[Int]):MatrixInt=
what match{caseRow=> replaceRow(where, newValue)caseColumn=> replaceCol(where, newValue)}// At REPL:
scala> a(Row,2)= a.row(1)<console>:13: error: not found: value Row
a(Row,2)= a.row(1)^
scala> a(Dimension.Row,2)= a.row(1)
res1: teste.MatrixInt=/100\|010|\010/
scala>importDimension._
importDimension._
scala> a(Row,2)= a.row(1)
res2: teste.MatrixInt=/100\|010|\010/
Infelizmente, isso não garante que todas as correspondências sejam contabilizadas. Se eu esquecesse de colocar Row ou Column na partida, o compilador Scala não teria me avisado. Isso me dá algum tipo de segurança, mas não tanto quanto se pode ganhar.
Você pode se perguntar, então, por que alguma vez usar uma Enumeração em vez de objetos de caso. Por uma questão de fato, os objetos de caso têm vantagens muitas vezes, como aqui. A classe Enumeration, no entanto, possui muitos métodos de coleção, como elementos (iterador no Scala 2.8), que retornam um iterador, mapa, flatMap, filtro etc.
Esta resposta é essencialmente uma parte selecionada deste artigo no meu blog.
"... não aceitando não-símbolos onde um símbolo é esperado"> Suponho que você queira dizer que Symbolinstâncias não podem ter espaços ou caracteres especiais. A maioria das pessoas quando se encontra com a Symbolclasse provavelmente pensa assim, mas na verdade está incorreta. Symbol("foo !% bar -* baz")compila e funciona perfeitamente bem. Em outras palavras, você pode criar perfeitamente Symbolinstâncias envolvendo qualquer string (você simplesmente não pode fazê-lo com o açúcar sintático "coma único"). A única coisa que Symbolgarante é a singularidade de qualquer símbolo, tornando marginalmente mais rápido comparar e comparar.
Régis Jean-Gilles
@ RégisJean-Gilles Não, quero dizer que você não pode passar um String, por exemplo, como argumento para um Symbolparâmetro.
Daniel C. Sobral
Sim, eu entendi essa parte, mas é um ponto discutível se você substituir Stringpor outra classe que é basicamente um invólucro em torno de uma string e pode ser livremente convertida em ambas as direções (como é o caso Symbol). Eu acho que foi isso que você quis dizer ao dizer "Não lhe dará nenhum tipo de segurança", mas não ficou muito claro, dado que o OP solicitou explicitamente soluções seguras para o tipo. Eu não tinha certeza se, no momento em que escrevi, você sabia que não só não é seguro quanto ao tipo, porque essas não são enumerações, mas tambémSymbol nem garantem que o argumento passado não tenha caracteres especiais.
Régis Jean-Gilles
1
Para elaborar, quando você diz "não aceitar não-símbolos onde um símbolo é esperado", ele pode ser lido como "não aceitando valores que não são instâncias do Symbol" (o que é obviamente verdadeiro) ou "não aceitando valores que não são" identificador-like simples cordas, aka 'símbolos'"(que não é verdade, e é um equívoco que praticamente ninguém tem a primeira vez que encontramos símbolos Scala, devido ao fato de que o primeiro encontro é que o especial 'foonotação que faz obstam cadeias não identificadoras). Esse é um equívoco que eu queria dissipar para qualquer futuro leitor.
Régis Jean-Gilles
@ RégisJean-Gilles eu quis dizer o primeiro, o que é obviamente verdadeiro. Quero dizer, obviamente é verdade para quem está acostumado a digitar estática. Naquela época, havia muita discussão sobre os méritos relativos da digitação estática e "dinâmica", e muitas pessoas interessadas no Scala vinham de um background de digitação dinâmico, então achei que não era preciso dizer isso. Eu nem pensaria em fazer esse comentário hoje em dia. Pessoalmente, acho que o símbolo de Scala é feio e redundante, e nunca o uso. Voto seu último comentário, já que é um bom argumento.
Daniel C. Sobral
52
Uma maneira um pouco menos detalhada de declarar enumerações nomeadas:
Obviamente, o problema aqui é que você precisará manter a ordem dos nomes e vals sincronizados, o que é mais fácil se os nomes e val forem declarados na mesma linha.
Isso parece mais limpo à primeira vista, mas tem a desvantagem de exigir que o mantenedor mantenha o oder de ambas as listas em sincronia. Para o exemplo dos dias da semana, não parece provável. Mas, em geral, o novo valor pode ser inserido ou um excluído e as duas listas podem estar fora de sincronia; nesse caso, erros sutis podem ser introduzidos.
Brent Faust
1
Pelo comentário anterior, o risco é que as duas listas diferentes podem silenciosamente ficar fora de sincronia. Embora não seja um problema para o seu pequeno exemplo atual, se houver muito mais membros (como dezenas ou centenas), as chances das duas listas ficarem silenciosamente fora de sincronia são substancialmente mais altas. O scala.Enumeration também não pode se beneficiar dos avisos / erros exaustivos do padrão de tempo de compilação do Scala. Eu criei uma resposta StackOverflow que contém uma solução realizando uma verificação de tempo de execução para garantir as duas listas permanecem em sincronia: stackoverflow.com/a/25923651/501113
chaotic3quilibrium
17
Você pode usar uma classe abstrata selada em vez da enumeração, por exemplo:
Característica selada com objetos de caso também é uma possibilidade.
Ashalynd
2
O padrão "traço selado + objetos de caso" possui problemas detalhados em uma resposta do StackOverflow. No entanto, eu fiz descobrir como resolver todas as questões relacionadas com este padrão que também é coberto com a thread: stackoverflow.com/a/25923651/501113
chaotic3quilibrium
7
acabou de descobrir o enumerado . é incrível e igualmente incrível, não é mais conhecido!
Depois de fazer uma extensa pesquisa sobre todas as opções em torno de "enumerações" no Scala, publiquei uma visão geral muito mais completa desse domínio em outro thread do StackOverflow . Ele inclui uma solução para o padrão "traço selado + objeto de caso" em que resolvi o problema de pedido de inicialização de classe / objeto da JVM.
Projeto é realmente bom com exemplos e documentação
Apenas este exemplo de seus documentos deve interessá-lo
import enumeratum._
sealedtraitGreetingextendsEnumEntryobjectGreetingextendsEnum[Greeting]{/*
`findValues` is a protected method that invokes a macro to find all `Greeting` object declarations inside an `Enum`
You use it to implement the `val values` member
*/val values = findValues
caseobjectHelloextendsGreetingcaseobjectGoodByeextendsGreetingcaseobjectHiextendsGreetingcaseobjectByeextendsGreeting}// Object Greeting has a `withName(name: String)` methodGreeting.withName("Hello")// => res0: Greeting = HelloGreeting.withName("Haro")// => java.lang.IllegalArgumentException: Haro is not a member of Enum (Hello, GoodBye, Hi, Bye)// A safer alternative would be to use `withNameOption(name: String)` method which returns an Option[Greeting]Greeting.withNameOption("Hello")// => res1: Option[Greeting] = Some(Hello)Greeting.withNameOption("Haro")// => res2: Option[Greeting] = None// It is also possible to use strings case insensitivelyGreeting.withNameInsensitive("HeLLo")// => res3: Greeting = HelloGreeting.withNameInsensitiveOption("HeLLo")// => res4: Option[Greeting] = Some(Hello)// Uppercase-only strings may also be usedGreeting.withNameUppercaseOnly("HELLO")// => res5: Greeting = HelloGreeting.withNameUppercaseOnlyOption("HeLLo")// => res6: Option[Greeting] = None// Similarly, lowercase-only strings may also be usedGreeting.withNameLowercaseOnly("hello")// => res7: Greeting = HelloGreeting.withNameLowercaseOnlyOption("hello")// => res8: Option[Greeting] = Some(Hello)
Respostas:
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
Exemplo de uso
fonte
Devo dizer que o exemplo copiado da documentação do Scala por skaffman acima é de utilidade limitada na prática (você também pode usar
case object
s).Para obter algo parecido com um Java
Enum
(ou seja, com métodostoString
evalueOf
métodos sensíveis - talvez você esteja mantendo os valores de enum em um banco de dados), é necessário modificá-lo um pouco. Se você usou o código do skaffman :Considerando que usando a seguinte declaração:
Você obtém resultados mais sensatos:
fonte
valueOf
A substituição de @macias éwithName
, que não retorna uma opção e gera um NSE se não houver correspondência. O que!Map[Weekday.Weekday, Long]
e adicionar um valor, digamosMon
que o compilador lança um erro de tipo inválido. Weekday.Weekday esperado Valor encontrado? Por que isso acontece?Existem muitas maneiras de fazer.
1) Use símbolos. No entanto, ele não fornecerá nenhum tipo de segurança, além de não aceitar não-símbolos, onde um símbolo é esperado. Só estou mencionando aqui por exaustividade. Aqui está um exemplo de uso:
2) Usando classe
Enumeration
:ou, se você precisar serializar ou exibi-lo:
Isso pode ser usado assim:
Infelizmente, isso não garante que todas as correspondências sejam contabilizadas. Se eu esquecesse de colocar Row ou Column na partida, o compilador Scala não teria me avisado. Isso me dá algum tipo de segurança, mas não tanto quanto se pode ganhar.
3) Objetos de caso:
Agora, se eu deixar de fora um caso em a
match
, o compilador me avisará:É usado praticamente da mesma maneira e nem precisa de
import
:Você pode se perguntar, então, por que alguma vez usar uma Enumeração em vez de objetos de caso. Por uma questão de fato, os objetos de caso têm vantagens muitas vezes, como aqui. A classe Enumeration, no entanto, possui muitos métodos de coleção, como elementos (iterador no Scala 2.8), que retornam um iterador, mapa, flatMap, filtro etc.
Esta resposta é essencialmente uma parte selecionada deste artigo no meu blog.
fonte
Symbol
instâncias não podem ter espaços ou caracteres especiais. A maioria das pessoas quando se encontra com aSymbol
classe provavelmente pensa assim, mas na verdade está incorreta.Symbol("foo !% bar -* baz")
compila e funciona perfeitamente bem. Em outras palavras, você pode criar perfeitamenteSymbol
instâncias envolvendo qualquer string (você simplesmente não pode fazê-lo com o açúcar sintático "coma único"). A única coisa queSymbol
garante é a singularidade de qualquer símbolo, tornando marginalmente mais rápido comparar e comparar.String
, por exemplo, como argumento para umSymbol
parâmetro.String
por outra classe que é basicamente um invólucro em torno de uma string e pode ser livremente convertida em ambas as direções (como é o casoSymbol
). Eu acho que foi isso que você quis dizer ao dizer "Não lhe dará nenhum tipo de segurança", mas não ficou muito claro, dado que o OP solicitou explicitamente soluções seguras para o tipo. Eu não tinha certeza se, no momento em que escrevi, você sabia que não só não é seguro quanto ao tipo, porque essas não são enumerações, mas tambémSymbol
nem garantem que o argumento passado não tenha caracteres especiais.'foo
notação que faz obstam cadeias não identificadoras). Esse é um equívoco que eu queria dissipar para qualquer futuro leitor.Uma maneira um pouco menos detalhada de declarar enumerações nomeadas:
Obviamente, o problema aqui é que você precisará manter a ordem dos nomes e vals sincronizados, o que é mais fácil se os nomes e val forem declarados na mesma linha.
fonte
Você pode usar uma classe abstrata selada em vez da enumeração, por exemplo:
fonte
acabou de descobrir o enumerado . é incrível e igualmente incrível, não é mais conhecido!
fonte
Depois de fazer uma extensa pesquisa sobre todas as opções em torno de "enumerações" no Scala, publiquei uma visão geral muito mais completa desse domínio em outro thread do StackOverflow . Ele inclui uma solução para o padrão "traço selado + objeto de caso" em que resolvi o problema de pedido de inicialização de classe / objeto da JVM.
fonte
Dotty (Scala 3) terá enumerações nativas suportadas. Confira aqui e aqui .
fonte
Em Scala, é muito confortável com https://github.com/lloydmeta/enumeratum
Projeto é realmente bom com exemplos e documentação
Apenas este exemplo de seus documentos deve interessá-lo
fonte