Apa cara terbaik untuk menginisialisasi anggota statis yang kompleks di Java?

Tujuan saya adalah memiliki objek Properties statis pribadi di kelas saya, untuk bertindak sebagai default saat membuat objek Properties lain yang diperlukan oleh aplikasi saya. Implementasi saat ini terlihat seperti ini:

public class MyClass {
    private static Properties DEFAULT_PROPERTIES = new Properties();

    static {
        try {
           DEFAULT_PROPERTIES.load(
               MyClass.class.getResourceAsStream("myclass.properties"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
 }

Melihatnya, itu berhasil, tetapi rasanya tidak benar.

Bagaimana Anda melakukannya?


person Igor    schedule 08.01.2010    source sumber


Jawaban (3)


Pada dasarnya ada dua cara. Cara pertama adalah menggunakan blok statis seperti yang Anda tunjukkan (tetapi kemudian dengan ExceptionInInitializerError bukannya RuntimeException). Cara kedua adalah menggunakan metode statis yang Anda panggil langsung saat deklarasi:

private static Properties DEFAULT_PROPERTIES = getDefaultProperties();

private static Properties getDefaultProperties() {
    Properties properties = new Properties();
    try {
        properties.load(MyClass.class.getResourceAsStream("myclass.properties"));
    } catch (IOException e) {
        throw new ConfigurationException("Cannot load properties file", e);
    }
    return properties;
}

ConfigurationException bisa saja berupa kelas khusus Anda yang memperluas RuntimeException.

Saya pribadi lebih memilih blok static karena tidak masuk akal memiliki metode yang dieksekusi hanya sekali seumur hidupnya. Namun jika Anda memfaktorkan ulang metode tersebut sehingga memerlukan nama file dan dapat digunakan kembali secara global, maka itu akan lebih disukai.

private static Properties DEFAULT_PROPERTIES = SomeUtil.getProperties("myclass.properties");

// Put this in a SomeUtil class.
public static Properties getProperties(String filename) {
    Properties properties = new Properties();
    try {
        properties.load(Thread.currentThread().getContextClassLoader().getResourceAsStream(filename));
    } catch (IOException e) {
        throw new ConfigurationException("Cannot load " + filename, e);
    }
    return properties;
}
person BalusC    schedule 08.01.2010
comment
Ini sebenarnya cocok dengan masalah saya, karena saya akan membuat lebih banyak kelas dengan pola yang sama, jadi masuk akal untuk membuat kelas abstrak dan mengisinya dengan metode getProperties(), sehingga dapat digunakan kembali di semua subkelas saya. Sangat informatif, terima kasih! - person Igor; 08.01.2010
comment
Perhatikan bahwa saya mengubah cara memuat file properti dengan menggunakan Thread#getContextClassLoader(). Jangan lupa untuk mengubahnya di kode Anda juga. - person BalusC; 08.01.2010
comment
Apakah ini akan memuat sumber daya dalam paket yang sama dengan panggilan kelas getProperties()? - person Igor; 08.01.2010
comment
Saya tidak yakin apakah saya memahami Anda. Apa pun yang terjadi, semuanya tidak bergantung pada paket. Anda dapat menentukan paket dalam nama file. Cara yang ditingkatkan hanya bersifat independen terhadap kelas dan memanfaatkan pemuat kelas yang telah memuat kelas pemanggil. Dengan cara ini Anda memastikan bahwa sumber daya tidak pernah mengembalikan null, yang mungkin terjadi jika kelas pemanggil tidak dimuat oleh pemuat kelas thread. - person BalusC; 08.01.2010
comment
Saya maksudkan bahwa org.example.MyClass.class.getResourceAsStream("myclass.properties") akan membuka sumber daya ROOT/org/example/myclass.properties, sedangkan menggunakan ClassLoader akan mencari sumber daya di direktori ROOT/. Apakah aku salah? - person Igor; 08.01.2010
comment
Oh lewat sini. Ya, cukup tentukan paketnya di nama file. - person BalusC; 08.01.2010

Alih-alih RuntimeException generik, saya akan melemparkan ExceptionInInitializerError, yang digunakan untuk tujuan ini. Dari dokumentasi API: "Menandakan bahwa pengecualian tak terduga telah terjadi di penginisialisasi statis."

person jarnbjo    schedule 08.01.2010
comment
Oh, aku tidak mengetahuinya. Terima kasih atas petunjuknya! - person Igor; 08.01.2010
comment
@jarnbjo Saya pikir akan sangat membantu jika Anda memberikan tautan ke dokumen API (dari ExceptionInitializer) juga: java.sun.com/j2se/1.5.0/docs/api/java/lang/ - person sateesh; 08.01.2010
comment
@sateesh: Jika pengembang Java tidak dapat menemukan kelas dalam dokumentasi API tanpa tautan dalam jawaban saya, inilah saatnya dia mempelajari cara melakukannya. - person jarnbjo; 08.01.2010

Tampaknya dapat diterima oleh saya; dimuat di penginisialisasi statis, itu dipanggil hanya ketika kelas direferensikan, dan hanya dipanggil sekali. Saya suka itu. Satu-satunya hal yang akan saya lakukan adalah menjadikannya final.

Ya, selain pengecualian. Saya akan mencoba dan menghindarinya (saya berpikir bahwa Anda harus menghindari pengecualian pada jenis inisialisasi tersebut, tetapi saya bisa saja salah dalam hal itu).

person Noon Silk    schedule 08.01.2010