Pertanyaan Mengapa tidak kelas benda kelas pertama di Jawa?


Dapatkah seseorang menjelaskan kepada saya mengapa kelas bukan objek kelas satu di Jawa? Ada pola-pola tertentu yang akan bekerja dengan sangat baik jika hal itu terjadi dan saya terus-menerus menulis kelas pabrik dengan getter dan setter dan hanya menambahkan cruft yang tidak perlu ke kelas hanya agar saya bisa berkeliling untuk memberikan contoh ke metode daripada kelas yang sebenarnya untuk faktor keluar generik potongan kode. Sebagai contoh:

public class AsyncBookSearch extends AsyncTask<String,BookItem,Void> {

    public ListView list;
    public AmazonItemAdapter<BookItem> adapter;
    public AsyncBookSearch(ListView l,AmazonItemAdapter<BookItem> ad) {
        list = l;
        adapter = ad;
    }

    @Override
    protected Void doInBackground(String... keywords) {
        // TODO Auto-generated method stub
        new BookSearch(keywords[0],list,adapter).parse();
        return null;
    }
}

Saya memiliki beberapa kelas seperti itu dan jika saya ingin membuat semuanya umum maka hal-hal di dalamnya doInBackground() akan menyebabkan beberapa metode tambahan dan jenis lain dari duplikasi dalam melewati argumen yang tidak akan menjadi masalah jika saya bisa menulis sebagai berikut:

public class AsyncItemSearch<T extends GenericItemSearch<S>,S extends GenericItem> extends AsyncTask<String,T,Void> {

    public ListView list;
    public AmazonItemAdapter<S> adapter;
    public AsyncBookSearch(ListView l,AmazonItemAdapter<S> ad) {
        list = l;
        adapter = ad;
    }

    @Override
    protected Void doInBackground(String... keywords) {
        // TODO Auto-generated method stub
        new T(keywords[0],list,adapter).parse();
        return null;
    }
}

Saat ini saya tidak bisa menulis kode seperti itu di Java. Saya harus memperkenalkan kopling yang tidak perlu ke hampir semua kelas yang terlibat dan ini hanya membuat hal-hal lebih berbelit-belit karena sekarang tidak hanya setiap contoh perlu khawatir tentang keadaannya sendiri tetapi juga tentang keadaan dalam objek yang sama sekali tidak terkait dengan tugasnya.


4
2018-02-14 19:15


asal


Jawaban:


Saya tidak berpikir pertanyaan Anda benar-benar tentang Kelas menjadi objek "kelas satu". Saya menduga itu benar-benar ada hubungannya dengan bagaimana java menangani generik. Saya pikir Anda perlu memahami penghapusan jenis: dalam contoh Anda, Anda pada dasarnya ingin menulis sesuatu seperti

void foo<T>{
  T bar = new T();
}

tetapi ini tidak mungkin, karena pada waktu berjalan, tidak ada informasi tentang apa sebenarnya kelas T, karena ketik penghapusan:

Ketika tipe generik dipakai,   compiler menerjemahkan jenis-jenis itu dengan   sebuah teknik yang disebut jenis penghapusan - a   proses di mana kompilator menghapus semua   informasi yang terkait dengan parameter jenis   dan ketik argumen dalam suatu kelas atau   metode. Ketik penghapusan memungkinkan Java   aplikasi yang menggunakan generik untuk   menjaga kompatibilitas biner dengan   Pustaka dan aplikasi Java itu   diciptakan sebelum generik.


9
2018-02-14 19:36



Kelas adalah kelas satu. Mereka adalah objek dari kelas Kelas. Mereka dapat ditugaskan ke variabel dan bidang, dilewatkan sebagai argumen, dan bahkan dibuat secara dinamis.

Masalah yang Anda hadapi adalah tipe generik yang tidak direvisi, mereka hanya terlihat pada waktu kompilasi. Selain itu, karena konstruktor tidak diwarisi, kecuali Anda tahu persis kelas mana yang ingin Anda buat, Anda tidak dapat mengetahui apakah konstruktor dengan argumen yang diinginkan ada. Salah satu cara untuk mengatasi masalah yang Anda hadapi adalah lulus kelas pabrik.

public class AsyncItemSearch<T extends GenericItemSearch<S>,S extends GenericItem> extends AsyncTask<String,T,Void> {

    public ListView list;
    public AmazonItemAdapter<S> adapter;
    public GenericItemSearchFactory<T> factory;
    public AsyncBookSearch(ListView l,AmazonItemAdapter<S> ad, GenericItemSearchFactory<T> factory) {
        list = l;
        adapter = ad;
        this.factory = factory;
    }

    @Override
    protected Void doInBackground(String... keywords) {
        this.factory.getInstance(keywords[0],list,adapter).parse();
        return null;
    }
}

2
2018-02-14 19:51



Kelas adalah objek kelas pertama di Jawa (kelas Kelas). Anda dapat menetapkan mereka ke variabel, meneruskan, kembali dari fungsi, dll. I berpikir Anda bermaksud menanyakan beberapa pertanyaan lain, tetapi saya tidak yakin pertanyaan itu. Mungkin sesuatu tentang penghapusan versus reifikasi tipe generik? Jawabannya mungkin menarik.

Contoh cruft yang Anda tulis dapat membantu.

Juga, pertimbangkan perbedaan antara pertanyaan seperti 'apakah X memiliki alasan' dan 'apakah X memiliki kegunaan yang baik'.

EDIT: (Menjawab komentar: "Saya belum melihat satu contoh kelas yang dilewatkan" (btw: Saya masih menganggapnya pertanyaan tentang penghapusan versus reifikasi, tetapi komentar khusus ini tidak menjawabnya). Saya terkejut Anda belum. Melewati kelas-kelas di Java sangat umum, beberapa contoh dari API yang sangat populer yang muncul di benak saya tanpa berpikir:

  • Pengelola entitas Hibernate / JPA mencari objek yang dipetakan berdasarkan kelasnya dan kunci utamanya; misalnya: Invoice i = entityManager.find(Invoice.class, 12l)

  • GWT menggunakan pabrik khusus untuk menyuntikkan kelas yang hanya ada di javascript yang dihasilkan (atau sebaliknya parametrized); Metode ini mengambil contoh kelas untuk dibuat; misalnya: Resource res1 = GWT.create(MyResources.class);

  • Spring's ApplicationContext memberi Anda kacang berdasarkan kelas yang Anda berikan kepada metode getBean; jadi Anda akan melakukannya: DataSource default = applicationContext.getBean(DataSource.class);

  • Instance kelas digunakan dalam kasus yang jarang terjadi ketika C # akan menggunakan refleksi (dan Java tidak dapat, karena menghapus jenis generik saat runtime); polanya kadang disebut "token kelas"

Dalam sebagian besar kasus di atas Anda akan melihat kelas literal (sebagai kelas objek kelas pertama memiliki literal di Jawa) dan bukan panggilan dinamis, tetapi itu sebagian besar karena sifat statis bahasa (dan programmer yang menggunakannya) . Biasanya dianggap hal yang baik untuk mengetahui jenis Anda pada saat kompilasi.

karena Java tidak mendukung generik saat runtime, Anda tidak dapat membuat kelas generik on-the-fly. Yang tidak ada bedanya, karena Anda dapat membuat kelas non-generik dan menggunakannya sebagai generik.

Menjawab komentar lain: membuat dan memodifikasi kelas saat runtime digunakan secara umum, tetapi terutama oleh infrastruktur Java: server aplikasi, pustaka. Lihatlah JMockit. Anda benar-benar dapat memodifikasi kelas yang ada atau mengganti metode on-the-fly-nya selama durasi pemanggilan metode.


2
2018-02-14 19:22



Saya tidak akan menganggap kelas sebagai objek kelas satu, tetapi saya akan mengatakannya Class adalah bagian dari API refleksi, karena Anda tidak dapat menggunakannya sebebas dalam bahasa lain yang lebih dinamis. Yaitu. Anda tidak dapat membuat instance baru tanpa refleksi.

Alasan utamanya adalah model meta java. Anda tidak dapat menimpa metode statis, dan konstruktor yang ada di kelas tidak selalu ada di subclassnya. Ini juga alasan mengapa kode Anda new T(keywords[0],list,adapter) tidak akan berfungsi, subclass mungkin tidak memiliki konstruktor seperti itu.

Jadi, di java, tidak ada gunanya untuk objek kelas karena Anda pasti perlu refleksi untuk memeriksa apakah kode Anda valid pada saat runtime.

Topik yang berbeda adalah parameter tipe generik. Anda tidak bisa melakukannya T.class karena jenius entah bagaimana bahasa hack di java (dan tidak ada yang dibandingkan dengan C ++ Templates). Alasan utama untuk ini adalah kompatibel dengan versi java yang lebih tua.

Namun, Anda dapat mengatasinya menggunakan API refleksi yang disebutkan di atas:

public Foo<T extends Bar> {

     private Class<T> barClass;

     public Foo(Class<T> barClass) {
         this.barClass = barClass;
     }

     public T createSomeBar(String arg) {
         try {
             // the bar contract says that subclasses must have such a constructor
             return barClass.getConstructor(String.class).newInstance(arg);
         } catch ... // the contract was violated, do proper handling
     }
}

2
2018-02-14 20:05



Kelas adalah barang kelas satu. Anda dapat memanggil metode "newinstance" di kelas untuk membuat instance, atau Anda dapat meminta objek konstruktor dan menggunakannya. Coba kode ini:

kelas publik AsyncItemSearch <T extends GenericItemSearch <S>, S extends GenericItem> memperluas AsyncTask <String, T, Void> {

    Konstruktor pribadi <T> searchConstructor;
    daftar ListView publik;
    adaptor publik AmazonItemAdapter <S>;

    iklan AsyncBookSearch publik (Kelas <T> theClass, ListView l, AmazonItemAdapter <S>) {
        daftar = l;
        adaptor = iklan;
        searchConstructor = theClass.getConstructor (String.class, ListView.class, AmazonItemAdapter <S> .class);
    }

    @Mengesampingkan
    protected Void doInBackground (String ... kata kunci) {
        // TODO Auto-generated method rintisan
        searchConstructor.newInstance (kata kunci [0], daftar, adaptor) .parse ();
        kembali nol;
    }
}

Sebut saja seperti ini:

AmazonItemAdapter <Book> amazonAdapter = new AmazonBookAdapter ();
AsyncItemSearch <BookSearch, Book> s =
    AsyncItemSearch baru <BookSearch, Book> (
        BookSearch.class, myListView, amazonAdapter
    );
s.doInBackground ("have", "at", "thee", "!");

1
2018-02-16 07:44