Menerapkan beberapa Antarmuka generik di java

10

Saya membutuhkan antarmuka yang meyakinkan saya metode tertentu, termasuk tanda tangan tertentu, tersedia. Sejauh ini adalah apa yang saya miliki:

public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

Masalah muncul ketika kelas harus bisa dipetakan ke beberapa entitas lain. Kasus yang ideal adalah ini (bukan java):

public class Something implements Mappable<A>, Mappable<B> {
    public A mapTo(A someObject) {...}
    public B mapTo(B someOtherObject) {...}
}

Apa yang akan menjadi cara terbaik untuk mencapai sisa ini sebagai "generik" mungkin?

estani
sumber

Jawaban:

10

Ini, tentu saja, bukan sesuatu yang dapat Anda lakukan karena Penghapusan Jenis . Saat runtime, Anda memiliki dua metode public Object mapTo(Object), yang jelas tidak bisa hidup berdampingan.

Sayangnya, apa yang Anda coba lakukan hanyalah di luar sistem tipe Java.

Dengan asumsi tipe generik Anda selalu merupakan tipe kelas pertama, dan bukan tipe generik itu sendiri, Anda bisa mencapai perilaku yang sama dengan menghadap ke luar dengan memiliki metode mapTo(Object, Class), yang akan memungkinkan Anda untuk melakukan inspeksi runtime dari kelas yang diberikan dan memutuskan perilaku mana yang akan digunakan. Jelas ini sangat tidak elegan - dan akan membutuhkan casting manual dari nilai pengembalian - tapi saya pikir itu yang terbaik yang dapat Anda lakukan. Jika tipe generik Anda sendiri generik, maka parameter generiknya akan dihapus juga dan Kelasnya akan sama, sehingga metode ini tidak akan berfungsi.

Namun, saya akan menunjuk ke jawaban @ Joachim juga, ini mungkin merupakan kasus di mana Anda dapat membagi perilaku menjadi komponen yang terpisah dan menghindari seluruh masalah.

Phoshi
sumber
3

Seperti yang Anda lihat, Anda tidak dapat mengimplementasikan antarmuka yang sama dua kali dengan parameter tipe yang berbeda (karena penghapusan: pada saat runtime mereka adalah antarmuka yang sama).

Juga, pendekatan ini melanggar prinsip tanggung jawab tunggal: kelas Anda harus fokus pada menjadi Something(apa pun artinya) dan tidak boleh melakukan pemetaan Aatau B menambah tugas itu.

Sepertinya Anda benar-benar harus memiliki Mapper<Something,A>dan Mapper<Something,B>. Dengan cara ini setiap kelas memiliki tanggung jawab tunggal yang jelas dan Anda tidak mengalami masalah dalam mengimplementasikan antarmuka yang sama dua kali.

Joachim Sauer
sumber
Yah, idenya adalah untuk membiarkan kelas bertanggung jawab untuk "mengubah" isinya ke objek lain. Lalu ada operator yang menangani mereka dengan cara agnostik Kelas, oleh karena itu persyaratan generik. Saya akan berpikir sedikit tentang mengekstraksi logika, meskipun itu sebenarnya berarti saya membagi kelas menjadi dua, tetapi mereka tetap berpasangan (akses ke lapangan harus diberikan, memodifikasi sebagian besar kali menyiratkan memodifikasi yang lain, dll)
estani
@estani: ya, mereka agak erat, tetapi mereka memiliki tanggung jawab yang berbeda. Juga pikirkan tentang ini: ketika Anda memperkenalkan kelas baru Cdan Anda ingin Somethingbisa dipetakan untuk itu, maka Anda perlu memodifikasi Something, yang terlalu banyak penggabungan. Hanya menambahkan yang baru SoemthingToCMappertidak terlalu mengganggu.
Joachim Sauer
1
Beri +1 pada ini - secara umum Anda harus mendukung komposisi daripada warisan jika Anda mencoba untuk mencapai apa yang diinginkan OP (di Jawa). Java 8 dengan metode default membuat ini lebih mudah - tetapi tidak semua orang bisa melompat di tepi pendarahan :-).
Martijn Verburg
0

Karena itu tidak diizinkan untuk mengimplementasikan antarmuka multi-multipel, Anda dapat mempertimbangkan penggunaan enkapsulasi. (contoh menggunakan java8 +)

// Mappable.java
public interface Mappable<M> {
    M mapTo(M mappableEntity);
}

// TwoMappables.java
public interface TwoMappables {
    default Mappable<A> mapableA() {
         return new MappableA();
    }

    default Mappable<B> mapableB() {
         return new MappableB();
    }

    class MappableA implements Mappable<A> {}
    class MappableB implements Mappable<B> {}
}

// Something.java
public class Something implements TwoMappables {
    // ... business logic ...
    mapableA().mapTo(A);
    mapableB().mapTo(B);
}

Silakan periksa di sini untuk info lebih lanjut & lebih banyak contoh: Bagaimana membuat kelas Java yang mengimplementasikan satu antarmuka dengan dua jenis generik? .

musim dingin
sumber
-1
public interface IMappable<S, T> {
    T MapFrom(S source);
}

// T - target
// S - source

Jika Anda ingin memetakan Pengguna ke UserDTO dan memetakan Pengguna ke UserViewModel, maka Anda akan membutuhkan dua implementasi terpisah. Jangan menumpuk semua logika ini ke dalam satu kelas - tidak masuk akal untuk melakukan itu.

Perbarui untuk membuat Joachim senang

public interface ITypeConverter<TSource, TDestination>
{
    TDestination Convert(TSource source);
}

Tapi sekarang kita berada di ranah Automapper ( http://automapper.codeplex.com/wikipage?title=Custom%20Type%20Converters )

CodeART
sumber
Saya tidak berpikir IMappableitu nama yang baik untuk sesuatu yang memetakan hal-hal lain. Mapper(atau IMapper, jika Anda harus ;-)) mungkin lebih benar. (Omong-omong: tidak, itu bukan downvote saya).
Joachim Sauer
Saya telah mengambil apa yang ada di pertanyaan dan diawali dengan huruf I untuk menyoroti fakta bahwa itu adalah antarmuka. Saya memecahkan masalah desain sesuai pertanyaan, bukan masalah penamaan.
CodeART
1
maaf, tapi menurut saya orang tidak bisa "memecahkan" masalah desain dan mengabaikan penamaan. Desain berarti struktur yang dapat dimengerti. Penamaan yang salah adalah masalah untuk dipahami.
Joachim Sauer
Pembaruan harus
menenangkan
1
@ CodeART Jika saya mengerti jawaban Anda dengan benar itu menyiratkan "MapFrom" (yang harus diturunkan ;-) menciptakan objek. Dalam kasus saya itu hanya mengisi informasi tentang objek yang sudah dibuat.
estani