Scala tidak memiliki tipe-aman enumseperti yang dimiliki Java. Diberikan seperangkat konstanta terkait, apa yang akan menjadi cara terbaik di Scala untuk mewakili konstanta tersebut?
Serius, Aplikasi tidak boleh digunakan. Itu TIDAK diperbaiki; kelas baru, App, diperkenalkan, yang tidak memiliki masalah yang disebutkan oleh Schildmeijer. Begitu juga "object foo extends App {...}" Dan Anda memiliki akses langsung ke argumen baris perintah melalui variabel args.
AmigoNico
scala.Enumeration (yang Anda gunakan dalam contoh kode "object WeekDay" di atas) tidak menawarkan pencocokan pola yang lengkap. Saya telah meneliti semua pola enumerasi yang berbeda yang saat ini sedang digunakan di Scala dan memberikan dan ikhtisar dari mereka dalam jawaban StackOverflow ini (termasuk pola baru yang menawarkan yang terbaik dari kedua scala.Penelitian dan pola "disegel sifat + objek objek" pola: stackoverflow. com / a / 25923651/501113
chaotic3quilibrium
377
Saya harus mengatakan bahwa contoh yang disalin dari dokumentasi Scala oleh skaffman di atas adalah utilitas terbatas dalam praktek (Anda mungkin juga menggunakan case objects).
Untuk mendapatkan sesuatu yang paling mirip dengan Java Enum(yaitu dengan metode yang masuk akal toStringdan valueOf- mungkin Anda mempertahankan nilai enum ke database) Anda perlu sedikit memodifikasinya. Jika Anda menggunakan kode skaffman :
@macias valueOfpengganti withName, yang tidak mengembalikan Opsi, dan melempar NSE jika tidak ada kecocokan. Apa itu!
Bluu
6
@Bluu Anda dapat menambahkan valueOf sendiri: def valueOf (name: String) = WeekDay.values.find (_. ToString == name) untuk memiliki Opsi
Centang
@ centr Ketika saya mencoba membuat Map[Weekday.Weekday, Long]dan menambahkan nilai katakan Monpadanya kompiler melempar kesalahan jenis tidak valid. Diharapkan hari kerja. Setiap hari nilai ditemukan? Mengapa ini terjadi?
Sohaib
@Sohaib Seharusnya Peta [Weekday.Value, Long].
centr
99
Ada banyak cara untuk melakukannya.
1) Gunakan simbol. Namun, itu tidak akan memberi Anda keamanan jenis apa pun, selain tidak menerima non-simbol di mana simbol diharapkan. Saya hanya menyebutkannya di sini untuk kelengkapan. Berikut ini contoh penggunaannya:
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/
Sayangnya, itu tidak memastikan bahwa semua pertandingan dicatat. Jika saya lupa memasukkan Baris atau Kolom dalam pertandingan, kompiler Scala tidak akan memperingatkan saya. Jadi itu memberi saya beberapa jenis keamanan, tetapi tidak sebanyak yang bisa diperoleh.
Jadi, Anda mungkin bertanya-tanya, mengapa pernah menggunakan Enumerasi alih-alih objek kasus. Faktanya, objek kasus memang memiliki kelebihan berkali-kali, seperti di sini. Kelas Enumerasi, bagaimanapun, memiliki banyak metode Koleksi, seperti elemen (iterator pada Scala 2.8), yang mengembalikan Iterator, peta, flatMap, filter, dll.
Jawaban ini pada dasarnya adalah bagian yang dipilih dari artikel ini di blog saya.
"... tidak menerima non-simbol di mana simbol diharapkan"> Saya menduga Anda berarti bahwa Symbolinstance tidak dapat memiliki spasi atau karakter khusus. Kebanyakan orang ketika pertama kali bertemu Symbolkelas mungkin berpikir begitu, tetapi sebenarnya tidak benar. Symbol("foo !% bar -* baz")kompilasi dan berjalan dengan sangat baik. Dengan kata lain Anda dapat dengan sempurna membuat Symbolinstance yang membungkus string apa pun (Anda tidak bisa melakukannya dengan gula sintaksis "koma tunggal"). Satu-satunya hal yang Symbolmenjamin adalah keunikan simbol yang diberikan, membuatnya sedikit lebih cepat untuk membandingkan dan mencocokkan.
Régis Jean-Gilles
@ RégisJean-Gilles Tidak, maksud saya Anda tidak dapat melewatkan String, misalnya, sebagai argumen ke Symbolparameter.
Daniel C. Sobral
Ya, saya mengerti bagian itu, tetapi itu adalah poin yang cukup bisa diperdebatkan jika Anda mengganti Stringdengan kelas lain yang pada dasarnya adalah pembungkus di sekitar string dan dapat dikonversi secara bebas di kedua arah (seperti halnya untuk Symbol). Saya kira itulah yang Anda maksud ketika mengatakan "Itu tidak akan memberi Anda keamanan jenis apa pun", hanya saja tidak begitu jelas mengingat bahwa OP secara eksplisit meminta solusi jenis aman. Saya tidak yakin apakah pada saat penulisan Anda tahu bahwa bukan saja itu bukan tipe aman karena itu sama sekali bukan enum, tetapi jugaSymbol tidak menjamin bahwa argumen yang disahkan tidak akan memiliki karakter khusus.
Régis Jean-Gilles
1
Untuk menguraikan, ketika Anda mengatakan "tidak menerima non-simbol di mana simbol diharapkan", itu dapat dibaca sebagai "tidak menerima nilai yang bukan contoh dari Simbol" (yang jelas benar) atau "tidak menerima nilai yang tidak string seperti identifier, alias 'simbol' "(yang tidak benar, dan merupakan kesalahpahaman bahwa hampir semua orang memiliki pertama kali kita menjumpai simbol scala, karena fakta bahwa pertemuan pertama adalah melalui 'foonotasi khusus yang tidak menghalangi string bukan pengidentifikasi). Inilah kesalahpahaman yang ingin saya singkirkan untuk pembaca di masa depan.
Régis Jean-Gilles
@ RégisJean-Gilles yang saya maksud adalah yang pertama, yang jelas benar. Maksudku, itu jelas benar bagi siapa pun yang terbiasa mengetik statis. Saat itu ada banyak diskusi tentang manfaat relatif dari pengetikan statis dan "dinamis", dan banyak orang yang tertarik pada Scala berasal dari latar belakang pengetikan yang dinamis, jadi saya pikir itu tidak sesuai tanpa mengatakan. Saya bahkan tidak akan berpikir untuk membuat pernyataan itu saat ini. Secara pribadi, saya pikir Scala's Symbol jelek dan berlebihan, dan tidak pernah menggunakannya. Saya membatalkan komentar terakhir Anda, karena itu poin yang bagus.
Daniel C. Sobral
52
Cara mendeklarasikan enumerasi dengan nama yang sedikit kurang jelas:
Tentu saja masalahnya di sini adalah bahwa Anda harus menjaga agar urutan nama dan vokal tetap sinkron yang lebih mudah dilakukan jika nama dan val dinyatakan pada baris yang sama.
Sepintas ini terlihat lebih bersih, tetapi memiliki kerugian karena mengharuskan pengelola untuk menjaga agar kedua daftar tetap sinkron. Untuk contoh hari dalam seminggu, sepertinya tidak mungkin. Tetapi secara umum, nilai baru bisa dimasukkan, atau satu dihapus dan dua daftar bisa tidak sinkron, dalam hal ini, bug halus dapat diperkenalkan.
Brent Faust
1
Per komentar sebelumnya, risikonya adalah dua daftar yang berbeda dapat secara diam-diam tidak sinkron. Meskipun ini bukan masalah bagi contoh kecil Anda saat ini, jika ada lebih banyak anggota (seperti dalam puluhan hingga ratusan), kemungkinan kedua daftar secara diam-diam tidak sinkron secara substansial lebih tinggi. Juga scala.Enumerasi tidak dapat mengambil manfaat dari peringatan / kesalahan pencocokan pola kompilasi waktu lengkap Scala. Saya telah membuat jawaban StackOverflow yang berisi solusi melakukan pemeriksaan runtime untuk memastikan dua daftar tetap sinkron: stackoverflow.com/a/25923651/501113
chaotic3quilibrium
17
Anda bisa menggunakan kelas abstrak tersegel alih-alih enumerasi, misalnya:
Sifat tertutup dengan objek kasing juga kemungkinan.
Ashalynd
2
Pola "objek sifat case + tersegel" memiliki masalah yang saya detailkan dalam jawaban StackOverflow. Namun, saya menemukan cara untuk menyelesaikan semua masalah yang terkait dengan pola ini yang juga dibahas di utas: stackoverflow.com/a/25923651/501113
chaotic3quilibrium
7
baru saja menemukan enumeratum . itu sangat menakjubkan dan sama-sama luar biasa itu tidak lebih terkenal!
Setelah melakukan penelitian ekstensif pada semua opsi di sekitar "enumerasi" di Scala, saya memposting tinjauan yang jauh lebih lengkap dari domain ini pada utas StackOverflow lainnya . Ini termasuk solusi untuk pola "seal trait + case object" di mana saya telah memecahkan masalah pemesanan inisialisasi kelas / objek JVM.
Project sangat bagus dengan contoh dan dokumentasi
Contoh ini hanya dari dokumen mereka harus membuat Anda tertarik
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)
Jawaban:
http://www.scala-lang.org/docu/files/api/scala/Enumeration.html
Contoh penggunaan
sumber
Saya harus mengatakan bahwa contoh yang disalin dari dokumentasi Scala oleh skaffman di atas adalah utilitas terbatas dalam praktek (Anda mungkin juga menggunakan
case object
s).Untuk mendapatkan sesuatu yang paling mirip dengan Java
Enum
(yaitu dengan metode yang masuk akaltoString
danvalueOf
- mungkin Anda mempertahankan nilai enum ke database) Anda perlu sedikit memodifikasinya. Jika Anda menggunakan kode skaffman :Sedangkan menggunakan deklarasi berikut:
Anda mendapatkan hasil yang lebih masuk akal:
sumber
valueOf
penggantiwithName
, yang tidak mengembalikan Opsi, dan melempar NSE jika tidak ada kecocokan. Apa itu!Map[Weekday.Weekday, Long]
dan menambahkan nilai katakanMon
padanya kompiler melempar kesalahan jenis tidak valid. Diharapkan hari kerja. Setiap hari nilai ditemukan? Mengapa ini terjadi?Ada banyak cara untuk melakukannya.
1) Gunakan simbol. Namun, itu tidak akan memberi Anda keamanan jenis apa pun, selain tidak menerima non-simbol di mana simbol diharapkan. Saya hanya menyebutkannya di sini untuk kelengkapan. Berikut ini contoh penggunaannya:
2) Menggunakan kelas
Enumeration
:atau, jika Anda perlu membuat cerita bersambung atau menampilkannya:
Ini dapat digunakan seperti ini:
Sayangnya, itu tidak memastikan bahwa semua pertandingan dicatat. Jika saya lupa memasukkan Baris atau Kolom dalam pertandingan, kompiler Scala tidak akan memperingatkan saya. Jadi itu memberi saya beberapa jenis keamanan, tetapi tidak sebanyak yang bisa diperoleh.
3) Objek kasing:
Sekarang, jika saya meninggalkan kasing pada
match
, kompiler akan memperingatkan saya:Ini digunakan dengan cara yang hampir sama, dan bahkan tidak memerlukan
import
:Jadi, Anda mungkin bertanya-tanya, mengapa pernah menggunakan Enumerasi alih-alih objek kasus. Faktanya, objek kasus memang memiliki kelebihan berkali-kali, seperti di sini. Kelas Enumerasi, bagaimanapun, memiliki banyak metode Koleksi, seperti elemen (iterator pada Scala 2.8), yang mengembalikan Iterator, peta, flatMap, filter, dll.
Jawaban ini pada dasarnya adalah bagian yang dipilih dari artikel ini di blog saya.
sumber
Symbol
instance tidak dapat memiliki spasi atau karakter khusus. Kebanyakan orang ketika pertama kali bertemuSymbol
kelas mungkin berpikir begitu, tetapi sebenarnya tidak benar.Symbol("foo !% bar -* baz")
kompilasi dan berjalan dengan sangat baik. Dengan kata lain Anda dapat dengan sempurna membuatSymbol
instance yang membungkus string apa pun (Anda tidak bisa melakukannya dengan gula sintaksis "koma tunggal"). Satu-satunya hal yangSymbol
menjamin adalah keunikan simbol yang diberikan, membuatnya sedikit lebih cepat untuk membandingkan dan mencocokkan.String
, misalnya, sebagai argumen keSymbol
parameter.String
dengan kelas lain yang pada dasarnya adalah pembungkus di sekitar string dan dapat dikonversi secara bebas di kedua arah (seperti halnya untukSymbol
). Saya kira itulah yang Anda maksud ketika mengatakan "Itu tidak akan memberi Anda keamanan jenis apa pun", hanya saja tidak begitu jelas mengingat bahwa OP secara eksplisit meminta solusi jenis aman. Saya tidak yakin apakah pada saat penulisan Anda tahu bahwa bukan saja itu bukan tipe aman karena itu sama sekali bukan enum, tetapi jugaSymbol
tidak menjamin bahwa argumen yang disahkan tidak akan memiliki karakter khusus.'foo
notasi khusus yang tidak menghalangi string bukan pengidentifikasi). Inilah kesalahpahaman yang ingin saya singkirkan untuk pembaca di masa depan.Cara mendeklarasikan enumerasi dengan nama yang sedikit kurang jelas:
Tentu saja masalahnya di sini adalah bahwa Anda harus menjaga agar urutan nama dan vokal tetap sinkron yang lebih mudah dilakukan jika nama dan val dinyatakan pada baris yang sama.
sumber
Anda bisa menggunakan kelas abstrak tersegel alih-alih enumerasi, misalnya:
sumber
baru saja menemukan enumeratum . itu sangat menakjubkan dan sama-sama luar biasa itu tidak lebih terkenal!
sumber
Setelah melakukan penelitian ekstensif pada semua opsi di sekitar "enumerasi" di Scala, saya memposting tinjauan yang jauh lebih lengkap dari domain ini pada utas StackOverflow lainnya . Ini termasuk solusi untuk pola "seal trait + case object" di mana saya telah memecahkan masalah pemesanan inisialisasi kelas / objek JVM.
sumber
Dotty (Scala 3) akan memiliki enum asli yang didukung. Periksa di sini dan di sini .
sumber
Di Scala sangat nyaman dengan https://github.com/lloydmeta/enumeratum
Project sangat bagus dengan contoh dan dokumentasi
Contoh ini hanya dari dokumen mereka harus membuat Anda tertarik
sumber