Bagaimana Anda mendefinisikan kelas konstanta di Java?

90

Misalkan Anda perlu mendefinisikan kelas yang semuanya hanya memegang konstanta.

public static final String SOME_CONST = "SOME_VALUE";

Apa cara yang disukai untuk melakukan ini?

  1. Antarmuka
  2. Kelas Abstrak
  3. Kelas Akhir

Mana yang harus saya gunakan dan mengapa?


Klarifikasi untuk beberapa jawaban:

Enum - Saya tidak akan menggunakan enum, saya tidak menghitung apa pun, hanya mengumpulkan beberapa konstanta yang tidak terkait satu sama lain dengan cara apa pun.

Antarmuka - Saya tidak akan menetapkan kelas apa pun sebagai salah satu yang mengimplementasikan antarmuka. Hanya ingin menggunakan antarmuka untuk memanggil konstanta seperti: ISomeInterface.SOME_CONST.

Yuval Adam
sumber
Ada beberapa diskusi serupa di sini: stackoverflow.com/questions/320588/… . Saya akan menggunakan kelas terakhir dengan konstruktor pribadi sehingga tidak dapat dipakai.
Dan Dyer
2
Maaf, tapi "Saya tidak akan menggunakan enum" mengubah pertanyaan ini menjadi "apa cara terbaik untuk melakukan sesuatu yang bodoh?"
cletus
Saya tidak mengatakan Anda akan mengimplementasikan antarmuka. Tetapi tidak ada gunanya menggunakan antarmuka untuk melakukan itu. Jadi, ikuti kelas terakhir
:)
Apa masalahnya dengan Enum? Anda selalu dapat menggunakannya untuk mengumpulkan 'beberapa konstanta yang tidak terkait satu sama lain dengan cara apa pun'. Hmm?
gedevan
6
Secara konseptual, enum adalah pilihan yang buruk jika konstanta tidak terkait. Enum merepresentasikan nilai alternatif dari tipe yang sama. Konstanta ini bukan alternatif dan bahkan mungkin bukan tipe yang sama (beberapa mungkin string, beberapa bilangan bulat, dll.)
Dan Dyer

Jawaban:

94

Gunakan kelas terakhir. untuk kesederhanaan Anda kemudian dapat menggunakan impor statis untuk menggunakan kembali nilai-nilai Anda di kelas lain

public final class MyValues {
  public static final String VALUE1 = "foo";
  public static final String VALUE2 = "bar";
}

di kelas lain:

import static MyValues.*
//...

if(variable.equals(VALUE1)){
//...
}
pengguna54579
sumber
4
Di manakah manfaat membuat kelas terpisah di sini? Meskipun saya biasanya tidak menggunakan API Kalender sebagai contoh desain yang baik, tidak masalah dalam istilah "konstanta terkait kalender ada di kelas Kalender".
Jon Skeet
7
manfaatnya adalah tidak menduplikasi kode, jika Anda perlu menggunakan kembali konstanta di lebih dari satu kelas. Saya kira Anda dapat dengan mudah melihat keuntungan dari ini.
user54579
2
Mengapa Anda menduplikasi kode? Lihat saja kelas lainnya. Saya merasa relatif jarang bahwa sebuah konstanta benar-benar berdiri sendiri.
Jon Skeet
14
Untuk keterbacaan yang lebih baik, saya juga memiliki konstruktor default pribadi tanpa isi (dan komentar yang cocok).
Ran Biron
5
Jawaban yang cukup lama, dan komentar ini mungkin tidak pada tempatnya, tetapi saya akan menggunakannya VALUE1.equals(variable)karena menghindari NPE.
Aakash
38

Klarifikasi Anda menyatakan: "Saya tidak akan menggunakan enum, saya tidak menghitung apa pun, hanya mengumpulkan beberapa konstanta yang tidak terkait satu sama lain dengan cara apa pun."

Jika konstanta sama sekali tidak terkait satu sama lain, mengapa Anda ingin mengumpulkannya? Letakkan setiap konstanta di kelas yang paling terkait dengannya.

Jon Skeet
sumber
2
Jon - mereka terkait dalam arti semuanya memiliki fungsi yang sama. Namun, mereka bukan pencacahan apa pun ... Mereka tidak memegang properti bahwa suatu objek adalah "salah satu dari hal itu".
Yuval Adam
13
Baik. Dalam hal ini, letakkan saja mereka di kelas yang paling dekat dengan fungsionalitas yang terkait dengannya.
Jon Skeet
28

Saran saya (dalam urutan preferensi yang menurun):

1) Jangan lakukan itu . Buat konstanta di kelas sebenarnya yang paling relevan. Memiliki kelas / antarmuka 'bag of constants' tidak benar-benar mengikuti praktik terbaik OO.

Saya, dan semua orang, mengabaikan # 1 dari waktu ke waktu. Jika Anda akan melakukannya, maka:

2) kelas akhir dengan konstruktor privat Ini setidaknya akan mencegah siapa pun menyalahgunakan 'bag of constants' Anda dengan memperluas / menerapkannya untuk mendapatkan akses mudah ke konstanta. (Saya tahu Anda mengatakan Anda tidak akan melakukan ini - tetapi itu tidak berarti seseorang datang setelah Anda tidak mau)

3) antarmuka Ini akan berfungsi, tetapi bukan preferensi saya memberikan kemungkinan penyalahgunaan yang disebutkan di # 2.

Secara umum, hanya karena ini adalah konstanta bukan berarti Anda tidak harus tetap menerapkan prinsip oo normal padanya. Jika tidak ada kecuali satu kelas yang peduli tentang konstanta - itu harus privat dan di kelas itu. Jika hanya pengujian yang peduli tentang konstanta - pengujian harus berada dalam kelas pengujian, bukan kode produksi. Jika konstanta didefinisikan di banyak tempat (tidak hanya secara tidak sengaja sama) - refactor untuk menghilangkan duplikasi. Dan seterusnya - perlakukan mereka seperti Anda memperlakukan sebuah metode.

kenj0418
sumber
2
Masalah dari 1 adalah bahwa terkadang Anda akan menyuntikkan dalam kode Anda beberapa dependensi ke kelas tier lain hanya untuk mengambil konstanta.
amdev
Semua bidang dalam sebuah antarmuka secara implisit bersifat publik, statis, dan final
Kodebetter
@Kodebet Tetapi antarmuka dapat diperpanjang / diimplementasikan untuk menambahkan lebih banyak bidang.
Wit
12

Seperti yang dicatat Joshua Bloch dalam Effective Java:

  • Antarmuka sebaiknya hanya digunakan untuk menentukan tipe,
  • kelas abstrak tidak mencegah ketidakstabilan (mereka dapat dibuat subkelas, dan bahkan menyarankan bahwa kelas tersebut dirancang untuk dijadikan subkelas).

Anda dapat menggunakan Enum jika semua konstanta Anda terkait (seperti nama planet), meletakkan nilai konstanta di kelas yang terkait dengannya (jika Anda memiliki akses ke sana), atau menggunakan kelas utilitas yang tidak instan (tentukan konstruktor default pribadi) .

class SomeConstants
{
    // Prevents instanciation of myself and my subclasses
    private SomeConstants() {}

    public final static String TOTO = "toto";
    public final static Integer TEN = 10;
    //...
}

Kemudian, seperti yang telah dinyatakan, Anda dapat menggunakan impor statis untuk menggunakan konstanta Anda.

Sébastien RoccaSerra
sumber
7

Metode pilihan saya adalah tidak melakukan itu sama sekali. Umur konstanta cukup banyak mati ketika Java 5 memperkenalkan enum typesafe. Dan bahkan sebelum itu Josh Bloch menerbitkan versi (yang sedikit lebih bertele-tele) dari itu, yang bekerja pada Java 1.4 (dan sebelumnya).

Kecuali Anda memerlukan interoperabilitas dengan beberapa kode lama, sebenarnya tidak ada alasan untuk menggunakan konstanta String / integer lagi.

cletus
sumber
2
Jawaban yang bagus, contoh akan lebih baik.
amdev
3

Gunakan saja kelas terakhir.

Jika Anda ingin dapat menambahkan nilai lain gunakan kelas abstrak.

Tidak masuk akal menggunakan antarmuka, antarmuka seharusnya menentukan kontrak. Anda hanya ingin mendeklarasikan beberapa nilai konstan.

Megacan
sumber
3

Bukankah enum adalah pilihan terbaik untuk hal-hal semacam ini?

Boris Pavlović
sumber
2
99% dari waktu. Tapi tidak selalu.
L.Holanda
3

enums baik-baik saja. IIRC, satu item di Java efektif (2nd Ed) memiliki enumkonstanta yang menghitung opsi standar yang mengimplementasikan [kata kunci Java] interfaceuntuk nilai apa pun.

Preferensi saya adalah dengan menggunakan [Java kata kunci] interfacedi atas final classuntuk konstanta. Anda secara implisit mendapatkan file public static final. Beberapa orang akan berpendapat bahwa interfacememungkinkan pemrogram yang buruk untuk menerapkannya, tetapi pemrogram yang buruk akan menulis kode yang menyebalkan apa pun yang Anda lakukan.

Mana yang terlihat lebih baik?

public final class SomeStuff {
     private SomeStuff() {
         throw new Error();
     }
     public static final String SOME_CONST = "Some value or another, I don't know.";
}

Atau:

public interface SomeStuff {
     String SOME_CONST = "Some value or another, I don't know.";
}
Tom Hawtin - tackline
sumber
tetapi programmer yang buruk akan menulis kode yang menyebalkan apa pun yang Anda lakukan. Itu memang benar. Jika Anda dapat mengambil langkah untuk mengurangi pemrograman yang buruk, atau menghilangkannya sama sekali, maka Anda memiliki sedikit tanggung jawab untuk melakukannya; menjadi sejelas mungkin. Seorang programmer yang buruk tidak dapat memperpanjang kelas terakhir.
liltitus 27
1
@ liltitus27 Sampai batas tertentu saya setuju. Tetapi apakah Anda benar-benar ingin kode yang jelek hanya untuk menghentikan salah satu cara programmer yang buruk menulis kode yang buruk? Kode yang mereka hasilkan akan tetap tidak ada harapan, tetapi sekarang kode Anda kurang dapat dibaca.
Tom Hawtin - tackline
1

Atau 4. Letakkan mereka di kelas yang berisi logika yang paling banyak menggunakan konstanta

... maaf, tidak bisa menahan ;-)

Simon Groenewolt
sumber
3
Ini bukan ide yang bagus. Ini menciptakan ketergantungan antara kelas yang seharusnya tidak ada.
Yuval Adam
Kenapa tidak? Saya akan mengatakan bahwa kelas yang menggunakan konstanta yang sama memiliki ketergantungan juga ... Dan entah bagaimana pasti ada satu kelas yang paling bergantung pada konstanta ini.
Simon Groenewolt
Jika hanya ada satu kelas yang menggunakan konstanta, ini masuk akal.
Tom Hawtin - tackline
-1
  1. Salah satu kelemahan dari private constructor adalah adanya metode yang tidak dapat diuji.

  2. Konsep enum menurut sifatnya baik untuk diterapkan dalam tipe domain tertentu, menerapkannya pada konstanta desentralisasi terlihat tidak cukup baik

Konsep Enum adalah "Pencacahan adalah sekumpulan item yang terkait erat".

  1. Memperluas / mengimplementasikan antarmuka konstan adalah praktik yang buruk, sulit untuk memikirkan persyaratan untuk memperluas konstanta yang tidak dapat diubah alih-alih merujuknya secara langsung.

  2. Jika menerapkan alat berkualitas seperti SonarSource, ada aturan yang memaksa pengembang untuk menjatuhkan antarmuka konstan, ini adalah hal yang aneh karena banyak proyek menikmati antarmuka yang konstan dan jarang melihat hal-hal "memperluas" terjadi pada antarmuka yang konstan

Guizhou Feng
sumber
1
"private constructor adalah keberadaan metode tidak pernah bisa diuji": tidak benar. Jika Anda (atau atasan Anda) benar-benar paranoid tentang laporan cakupan kode menjadi 100%, Anda masih dapat menguji ini dengan menggunakan refleksi dan memastikan konstruktor mengeluarkan pengecualian. Tapi, IMHO, ini hanya formalitas dan tidak perlu diuji. Jika Anda (atau tim) hanya benar-benar peduli pada apa yang benar-benar penting, cakupan garis 100% tidak diperlukan.
L.Holanda