Bagaimana cara membuat metode pengembalian tipe generik?


589

Pertimbangkan contoh ini (khas dalam buku OOP):

Saya memiliki Animalkelas, di mana masing Animal- masing dapat memiliki banyak teman.
Dan subclass seperti Dog, Duck, Mousedll yang menambah perilaku tertentu seperti bark(), quack()dll

Inilah Animalkelasnya:

public class Animal {
    private Map<String,Animal> friends = new HashMap<>();

    public void addFriend(String name, Animal animal){
        friends.put(name,animal);
    }

    public Animal callFriend(String name){
        return friends.get(name);
    }
}

Dan inilah beberapa cuplikan kode dengan banyak pengetikan:

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

((Dog) jerry.callFriend("spike")).bark();
((Duck) jerry.callFriend("quacker")).quack();

Apakah ada cara saya bisa menggunakan obat generik untuk jenis kembali untuk menghilangkan typecasting, sehingga saya bisa mengatakan

jerry.callFriend("spike").bark();
jerry.callFriend("quacker").quack();

Berikut adalah beberapa kode awal dengan tipe pengembalian yang disampaikan ke metode sebagai parameter yang tidak pernah digunakan.

public<T extends Animal> T callFriend(String name, T unusedTypeObj){
    return (T)friends.get(name);        
}

Apakah ada cara untuk mengetahui tipe pengembalian saat runtime tanpa menggunakan parameter tambahan instanceof? Atau setidaknya dengan melewatkan kelas tipe bukan instance dummy.
Saya mengerti obat generik adalah untuk mengkompilasi jenis waktu, tetapi apakah ada solusi untuk ini?

Jawaban:


903

Anda dapat mendefinisikan callFrienddengan cara ini:

public <T extends Animal> T callFriend(String name, Class<T> type) {
    return type.cast(friends.get(name));
}

Kemudian menyebutnya seperti itu:

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

Kode ini memiliki manfaat tidak menghasilkan peringatan kompiler. Tentu saja ini benar-benar hanya versi terbaru casting dari masa pra-generik dan tidak menambah keamanan tambahan.


29
... tetapi masih belum memiliki jenis waktu kompilasi yang memeriksa antara parameter panggilan callFriend ().
David Schmitt

2
Ini adalah jawaban terbaik sejauh ini - tetapi Anda harus mengubah addFriend dengan cara yang sama. Itu membuatnya lebih sulit untuk menulis bug karena Anda membutuhkan kelas literal di kedua tempat.
Craig P. Motlin

@Jaider, tidak persis sama tetapi ini akan berfungsi: // publik Kelas Hewan T CallFriend <T> (nama string) di mana T: Animal {return friends [name] as T; } // Calling Class jerry.CallFriend <Dog> ("spike"). Bark (); jerry.CallFriend <Duck> ("quacker"). Quack ();
Nestor Ledon

124

Tidak. Kompilator tidak tahu tipe apa yang jerry.callFriend("spike")akan kembali. Selain itu, implementasi Anda hanya menyembunyikan gips dalam metode tanpa keamanan jenis tambahan. Pertimbangkan ini:

jerry.addFriend("quaker", new Duck());
jerry.callFriend("quaker", /* unused */ new Dog()); // dies with illegal cast

Dalam kasus khusus ini, membuat talk()metode abstrak dan menimpanya dengan tepat di subkelas akan memberi Anda jauh lebih baik:

Mouse jerry = new Mouse();
jerry.addFriend("spike", new Dog());
jerry.addFriend("quacker", new Duck());

jerry.callFriend("spike").talk();
jerry.callFriend("quacker").talk();

10
Sementara metode mmyers mungkin bekerja, saya pikir metode ini adalah pemrograman OO yang lebih baik dan akan menyelamatkan Anda dari beberapa masalah di masa depan.
James McMahon

1
Ini adalah cara yang benar untuk mencapai hasil yang sama. Perhatikan tujuannya adalah untuk mendapatkan perilaku spesifik kelas turunan pada saat runtime tanpa secara eksplisit menulis kode sendiri untuk melakukan pengecekan dan pengecoran tipe jelek. Metode yang diusulkan oleh @ laz berhasil, tetapi membuang tipe safety keluar jendela. Metode ini membutuhkan lebih sedikit baris kode (karena implementasi metode terlambat terikat dan dilihat saat runtime) tetapi tetap memungkinkan Anda dengan mudah mendefinisikan perilaku unik untuk setiap subkelas Hewan.
dcow

Tetapi pertanyaan aslinya bukan tentang keamanan jenis. Cara saya membacanya, penanya hanya ingin tahu apakah ada cara untuk meningkatkan obat generik agar tidak harus dilemparkan.
laz

2
@ Laz: ya, pertanyaan asli - seperti yang diajukan - bukan tentang keamanan jenis. Itu tidak mengubah fakta bahwa ada cara aman untuk mengimplementasikannya, menghilangkan kegagalan pemeran kelas. Lihat juga weblogs.asp.net/alex_papadimoulis/archive/2005/05/25/…
David Schmitt

3
Saya tidak setuju tentang itu, tapi kami berurusan dengan Java dan semua keputusan / kelemahan desainnya. Saya melihat pertanyaan ini sebagai hanya mencoba untuk mempelajari apa yang mungkin di generik Java, bukan sebagai xyproblem ( meta.stackexchange.com/questions/66377/what-is-the-xy-problem ) yang perlu direkayasa ulang. Seperti pola atau pendekatan apa pun, ada kalanya kode yang saya berikan sesuai dan kadang-kadang diperlukan sesuatu yang sama sekali berbeda (seperti yang Anda sarankan dalam jawaban ini).
laz

114

Anda bisa menerapkannya seperti ini:

@SuppressWarnings("unchecked")
public <T extends Animal> T callFriend(String name) {
    return (T)friends.get(name);
}

(Ya, ini adalah kode legal; lihat Java Generics: Tipe generik yang didefinisikan sebagai tipe pengembalian saja .)

Jenis kembali akan disimpulkan dari pemanggil. Namun, perhatikan @SuppressWarningsanotasi: yang memberi tahu Anda bahwa kode ini tidak aman untuk mengetik . Anda harus memverifikasinya sendiri, atau Anda bisa mendapatkannya ClassCastExceptionssaat runtime.

Sayangnya, cara Anda menggunakannya (tanpa menetapkan nilai kembali ke variabel sementara), satu-satunya cara untuk membuat kompiler senang adalah dengan menyebutnya seperti ini:

jerry.<Dog>callFriend("spike").bark();

Meskipun ini mungkin sedikit lebih baik daripada casting, Anda mungkin lebih baik memberikan metode Animalabstrak kepada kelas talk(), seperti yang dikatakan David Schmitt.


Metode chaining sebenarnya bukan niat. saya tidak keberatan menugaskan nilai ke variabel Subtyped dan menggunakannya. Terima kasih atas solusinya.
Sathish

ini berfungsi dengan baik saat melakukan chaining panggilan metode!
Hartmut P.

Saya sangat suka sintaks ini. Saya pikir dalam C # itu jerry.CallFriend<Dog>(...yang saya pikir terlihat lebih baik.
andho

Menarik bahwa fungsi JRE sendiri java.util.Collections.emptyList()diimplementasikan persis seperti ini, dan javadoc-nya mengiklankan dirinya sebagai typesafe.
Ti Strga

31

Pertanyaan ini sangat mirip dengan Butir 29 di Jawa Efektif - "Pertimbangkan wadah heterogen yang aman." Jawaban Laz adalah yang paling dekat dengan solusi Bloch. Namun, keduanya put dan get harus menggunakan Class literal untuk keamanan. Tanda tangan itu akan menjadi:

public <T extends Animal> void addFriend(String name, Class<T> type, T animal);
public <T extends Animal> T callFriend(String name, Class<T> type);

Di dalam kedua metode Anda harus memeriksa bahwa parameternya waras. Lihat Java Efektif dan Kelas javadoc untuk info lebih lanjut.


17

Selain itu, Anda dapat meminta metode untuk mengembalikan nilai dalam jenis yang diberikan dengan cara ini

<T> T methodName(Class<T> var);

Lebih banyak contoh di sini di dokumentasi Oracle Java


17

Ini adalah versi yang lebih sederhana:

public <T> T callFriend(String name) {
    return (T) friends.get(name); //Casting to T not needed in this case but its a good practice to do
}

Kode sepenuhnya berfungsi:

    public class Test {
        public static class Animal {
            private Map<String,Animal> friends = new HashMap<>();

            public void addFriend(String name, Animal animal){
                friends.put(name,animal);
            }

            public <T> T callFriend(String name){
                return (T) friends.get(name);
            }
        }

        public static class Dog extends Animal {

            public void bark() {
                System.out.println("i am dog");
            }
        }

        public static class Duck extends Animal {

            public void quack() {
                System.out.println("i am duck");
            }
        }

        public static void main(String [] args) {
            Animal animals = new Animal();
            animals.addFriend("dog", new Dog());
            animals.addFriend("duck", new Duck());

            Dog dog = animals.callFriend("dog");
            dog.bark();

            Duck duck = animals.callFriend("duck");
            duck.quack();

        }
    }

1
Apa Casting to T not needed in this case but it's a good practice to do. Maksud saya jika diurus dengan benar selama runtime apa artinya "praktik yang baik"?
Farid

Saya bermaksud mengatakan bahwa casting eksplisit (T) tidak diperlukan karena jenis pernyataan yang dikembalikan <T> pada deklarasi metode harus cukup
webjockey

9

Seperti yang Anda katakan lulus kelas akan baik-baik saja, Anda bisa menulis ini:

public <T extends Animal> T callFriend(String name, Class<T> clazz) {
   return (T) friends.get(name);
}

Dan kemudian gunakan seperti ini:

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

Tidak sempurna, tapi ini sejauh yang Anda dapatkan dengan obat generik Java. Ada cara untuk mengimplementasikan Typeafe Heterogenous Containers (THC) menggunakan Token Jenis Super , tetapi itu memiliki masalah sendiri lagi.


Maaf, tapi ini adalah jawaban yang persis sama dengan yang Anda miliki, jadi Anda menyalinnya atau dia menyalin Anda.
James McMahon

Itu cara yang rapi untuk lulus Type. Tapi tetap saja tidak aman seperti kata Schmitt. Saya masih bisa lulus kelas yang berbeda dan typecasting akan meledak. mmyers 2nd reply untuk mengatur Type in Return type sepertinya lebih baik
Sathish

4
Nemo, jika Anda memeriksa waktu posting Anda akan melihat kami mempostingnya cukup banyak pada saat yang sama. Juga, mereka tidak persis sama, hanya dua baris.
Fabian Steeg

@Fabian Saya memposting jawaban yang sama, tetapi ada perbedaan penting antara slide Bloch dan apa yang dipublikasikan di Java Efektif. Dia menggunakan Kelas <T> bukan TypeRef <T>. Tapi ini masih jawaban yang bagus.
Craig P. Motlin

8

Berdasarkan ide yang sama dengan Token Jenis Super, Anda bisa membuat id yang diketik untuk digunakan sebagai ganti string:

public abstract class TypedID<T extends Animal> {
  public final Type type;
  public final String id;

  protected TypedID(String id) {
    this.id = id;
    Type superclass = getClass().getGenericSuperclass();
    if (superclass instanceof Class) {
      throw new RuntimeException("Missing type parameter.");
    }
    this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
  }
}

Tapi saya pikir ini mungkin mengalahkan tujuan, karena Anda sekarang perlu membuat objek id baru untuk setiap string dan berpegang pada mereka (atau merekonstruksi mereka dengan informasi jenis yang benar).

Mouse jerry = new Mouse();
TypedID<Dog> spike = new TypedID<Dog>("spike") {};
TypedID<Duck> quacker = new TypedID<Duck>("quacker") {};

jerry.addFriend(spike, new Dog());
jerry.addFriend(quacker, new Duck());

Tetapi Anda sekarang dapat menggunakan kelas dengan cara yang Anda inginkan, tanpa gips.

jerry.callFriend(spike).bark();
jerry.callFriend(quacker).quack();

Ini hanya menyembunyikan parameter tipe di dalam id, meskipun itu berarti Anda dapat mengambil tipe dari pengenal nanti jika Anda mau.

Anda perlu menerapkan metode perbandingan dan hashing pada TypedID juga jika Anda ingin dapat membandingkan dua instance id yang identik.


8

"Apakah ada cara untuk mengetahui tipe pengembalian saat runtime tanpa parameter tambahan menggunakan instanceof?"

Sebagai solusi alternatif Anda bisa memanfaatkan pola Pengunjung seperti ini. Jadikan Hewan abstrak dan implementasikan Visitable:

abstract public class Animal implements Visitable {
  private Map<String,Animal> friends = new HashMap<String,Animal>();

  public void addFriend(String name, Animal animal){
      friends.put(name,animal);
  }

  public Animal callFriend(String name){
      return friends.get(name);
  }
}

Dikunjungi hanya berarti bahwa implementasi Hewan bersedia menerima pengunjung:

public interface Visitable {
    void accept(Visitor v);
}

Dan implementasi pengunjung dapat mengunjungi semua subclass dari hewan:

public interface Visitor {
    void visit(Dog d);
    void visit(Duck d);
    void visit(Mouse m);
}

Jadi misalnya implementasi Anjing akan terlihat seperti ini:

public class Dog extends Animal {
    public void bark() {}

    @Override
    public void accept(Visitor v) { v.visit(this); }
}

Kuncinya di sini adalah bahwa karena Anjing tahu jenisnya, ia dapat memicu metode kunjungan berlebih yang relevan dari pengunjung v dengan meneruskan "ini" sebagai parameter. Subclass lain akan menerapkan accept () dengan cara yang persis sama.

Kelas yang ingin memanggil metode khusus subkelas harus mengimplementasikan antarmuka Pengunjung seperti ini:

public class Example implements Visitor {

    public void main() {
        Mouse jerry = new Mouse();
        jerry.addFriend("spike", new Dog());
        jerry.addFriend("quacker", new Duck());

        // Used to be: ((Dog) jerry.callFriend("spike")).bark();
        jerry.callFriend("spike").accept(this);

        // Used to be: ((Duck) jerry.callFriend("quacker")).quack();
        jerry.callFriend("quacker").accept(this);
    }

    // This would fire on callFriend("spike").accept(this)
    @Override
    public void visit(Dog d) { d.bark(); }

    // This would fire on callFriend("quacker").accept(this)
    @Override
    public void visit(Duck d) { d.quack(); }

    @Override
    public void visit(Mouse m) { m.squeak(); }
}

Saya tahu ini jauh lebih banyak antarmuka dan metode daripada yang Anda tawar-menawar, tetapi ini adalah cara standar untuk menangani setiap subtipe tertentu dengan tepat nol instance of cek dan nol jenis gips. Dan itu semua dilakukan dalam mode agnostik bahasa standar sehingga tidak hanya untuk Java tetapi semua bahasa OO harus bekerja sama.


6

Tidak memungkinkan. Bagaimana Peta seharusnya tahu subclass Hewan mana yang akan didapat, hanya diberi kunci String?

Satu-satunya cara ini akan mungkin adalah jika setiap Hewan menerima hanya satu jenis teman (maka itu bisa menjadi parameter dari kelas Hewan), atau metode callFriend () mendapat parameter tipe. Tapi sepertinya Anda kehilangan titik pewarisan: Anda hanya bisa memperlakukan subkelas secara seragam saat menggunakan metode superclass secara eksklusif.


5

Saya telah menulis sebuah artikel yang berisi bukti konsep, kelas pendukung, dan kelas tes yang menunjukkan bagaimana Token Jenis Super dapat diambil oleh kelas Anda saat runtime. Singkatnya, ini memungkinkan Anda untuk mendelegasikan ke implementasi alternatif tergantung pada parameter umum aktual yang dilewatkan oleh pemanggil. Contoh:

  • TimeSeries<Double> mendelegasikan ke kelas batin pribadi yang menggunakan double[]
  • TimeSeries<OHLC> mendelegasikan ke kelas batin pribadi yang menggunakan ArrayList<OHLC>

Lihat: Menggunakan TypeTokens untuk mengambil parameter umum

Terima kasih

Richard Gomes - Blog


Memang, terima kasih telah berbagi wawasan Anda, artikel Anda benar-benar menjelaskan semuanya!
Yann-Gaël Guéhéneuc

3

Ada banyak jawaban yang bagus di sini, tetapi ini adalah pendekatan yang saya ambil untuk tes Appium di mana bekerja pada satu elemen dapat menghasilkan kondisi aplikasi yang berbeda berdasarkan pengaturan pengguna. Meskipun tidak mengikuti konvensi contoh OP, saya harap ini membantu seseorang.

public <T extends MobilePage> T tapSignInButton(Class<T> type) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    //signInButton.click();
    return type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
}
  • MobilePage adalah kelas super yang jenisnya diperluas yang berarti Anda dapat menggunakan salah satu dari anak-anaknya (ya)
  • type.getConstructor (Param.class, dll) memungkinkan Anda untuk berinteraksi dengan konstruktor tipe tersebut. Konstruktor ini harus sama antara semua kelas yang diharapkan.
  • newInstance mengambil variabel yang dideklarasikan yang ingin Anda sampaikan ke konstruktor objek baru

Jika Anda tidak ingin melempar kesalahan, Anda dapat menangkapnya seperti:

public <T extends MobilePage> T tapSignInButton(Class<T> type) {
    // signInButton.click();
    T returnValue = null;
    try {
       returnValue = type.getConstructor(AppiumDriver.class).newInstance(appiumDriver);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return returnValue;
}

Menurut pemahaman saya, ini adalah cara terbaik dan paling elegan untuk menggunakan Generics.
cbaldan

2

Tidak juga, karena seperti yang Anda katakan, kompiler hanya tahu bahwa callFriend () mengembalikan Hewan, bukan Anjing atau Bebek.

Tidak bisakah Anda menambahkan metode abstrak makeNoise () ke Animal yang akan diimplementasikan sebagai kulit kayu atau dukun oleh subkelasnya?


1
bagaimana jika hewan memiliki banyak metode yang bahkan tidak termasuk dalam tindakan umum yang dapat diabstraksikan? Saya membutuhkan ini untuk komunikasi antara subclass dengan tindakan yang berbeda di mana saya setuju dengan melewati Type, bukan sebuah instance.
Sathish

2
Anda benar-benar baru saja menjawab pertanyaan Anda sendiri - jika seekor hewan memiliki tindakan unik, maka Anda harus melemparkannya ke hewan tertentu itu. Jika seekor hewan memiliki tindakan yang dapat dikelompokkan dengan hewan lain, Anda dapat mendefinisikan metode abstrak atau virtual di kelas dasar dan menggunakannya.
Matt Jordan

2

Apa yang Anda cari di sini adalah abstraksi. Kode terhadap antarmuka lebih banyak dan Anda harus melakukan casting lebih sedikit.

Contoh di bawah ini dalam C # tetapi konsepnya tetap sama.

using System;
using System.Collections.Generic;
using System.Reflection;

namespace GenericsTest
{
class MainClass
{
    public static void Main (string[] args)
    {
        _HasFriends jerry = new Mouse();
        jerry.AddFriend("spike", new Dog());
        jerry.AddFriend("quacker", new Duck());

        jerry.CallFriend<_Animal>("spike").Speak();
        jerry.CallFriend<_Animal>("quacker").Speak();
    }
}

interface _HasFriends
{
    void AddFriend(string name, _Animal animal);

    T CallFriend<T>(string name) where T : _Animal;
}

interface _Animal
{
    void Speak();
}

abstract class AnimalBase : _Animal, _HasFriends
{
    private Dictionary<string, _Animal> friends = new Dictionary<string, _Animal>();


    public abstract void Speak();

    public void AddFriend(string name, _Animal animal)
    {
        friends.Add(name, animal);
    }   

    public T CallFriend<T>(string name) where T : _Animal
    {
        return (T) friends[name];
    }
}

class Mouse : AnimalBase
{
    public override void Speak() { Squeek(); }

    private void Squeek()
    {
        Console.WriteLine ("Squeek! Squeek!");
    }
}

class Dog : AnimalBase
{
    public override void Speak() { Bark(); }

    private void Bark()
    {
        Console.WriteLine ("Woof!");
    }
}

class Duck : AnimalBase
{
    public override void Speak() { Quack(); }

    private void Quack()
    {
        Console.WriteLine ("Quack! Quack!");
    }
}
}

Pertanyaan ini berkaitan dengan pengkodean bukan konsep.

2

Saya melakukan hal berikut di lib kontraktor saya:

public class Actor<SELF extends Actor> {
    public SELF self() { return (SELF)_self; }
}

subclassing:

public class MyHttpAppSession extends Actor<MyHttpAppSession> {
   ...
}

setidaknya ini berfungsi di dalam kelas saat ini dan ketika memiliki referensi yang diketik kuat. Beragam warisan bekerja, tetapi menjadi sangat rumit :)


1

Saya tahu ini adalah hal yang sama sekali berbeda dari yang diminta. Cara lain untuk menyelesaikan ini adalah refleksi. Maksud saya, ini tidak mengambil keuntungan dari Generics, tetapi ini memungkinkan Anda meniru, dalam beberapa hal, perilaku yang ingin Anda lakukan (membuat anjing menggonggong, membuat dukun bebek, dll.) Tanpa mengurus jenis casting:

import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;

abstract class AnimalExample {
    private Map<String,Class<?>> friends = new HashMap<String,Class<?>>();
    private Map<String,Object> theFriends = new HashMap<String,Object>();

    public void addFriend(String name, Object friend){
        friends.put(name,friend.getClass());
        theFriends.put(name, friend);
    }

    public void makeMyFriendSpeak(String name){
        try {
            friends.get(name).getMethod("speak").invoke(theFriends.get(name));
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    } 

    public abstract void speak ();
};

class Dog extends Animal {
    public void speak () {
        System.out.println("woof!");
    }
}

class Duck extends Animal {
    public void speak () {
        System.out.println("quack!");
    }
}

class Cat extends Animal {
    public void speak () {
        System.out.println("miauu!");
    }
}

public class AnimalExample {

    public static void main (String [] args) {

        Cat felix = new Cat ();
        felix.addFriend("Spike", new Dog());
        felix.addFriend("Donald", new Duck());
        felix.makeMyFriendSpeak("Spike");
        felix.makeMyFriendSpeak("Donald");

    }

}

1

bagaimana dengan

public class Animal {
private Map<String,<T extends Animal>> friends = new HashMap<String,<T extends Animal>>();

public <T extends Animal> void addFriend(String name, T animal){
    friends.put(name,animal);
}

public <T extends Animal> T callFriend(String name){
    return friends.get(name);
}

}


0

Ada pendekatan lain, Anda bisa mempersempit tipe kembali ketika Anda mengganti metode. Di setiap subclass Anda harus mengganti callFriend untuk mengembalikan subclass itu. Biayanya adalah beberapa deklarasi callFriend, tetapi Anda dapat mengisolasi bagian-bagian umum ke metode yang disebut secara internal. Ini tampaknya jauh lebih sederhana bagi saya daripada solusi yang disebutkan di atas, dan tidak perlu argumen tambahan untuk menentukan tipe pengembalian.


Saya tidak yakin apa yang Anda maksud dengan "mempersempit tipe pengembalian". Afaik, Java, dan sebagian besar bahasa yang diketik tidak membebani metode atau fungsi berdasarkan tipe kembali. Misalnya public int getValue(String name){}tidak bisa dibedakan dari public boolean getValue(String name){}sudut pandang kompiler. Anda perlu mengubah tipe parameter atau menambah / menghapus parameter agar kelebihan beban dapat dikenali. Mungkin aku hanya salah paham padamu ...
The One True Colter

dalam java, Anda dapat mengganti metode dalam subkelas dan menentukan tipe pengembalian yang lebih "sempit" (yaitu lebih spesifik). Lihat stackoverflow.com/questions/14694852/… .
FeralWhippet

-3
public <X,Y> X nextRow(Y cursor) {
    return (X) getRow(cursor);
}

private <T> Person getRow(T cursor) {
    Cursor c = (Cursor) cursor;
    Person s = null;
    if (!c.moveToNext()) {
        c.close();
    } else {
        String id = c.getString(c.getColumnIndex("id"));
        String name = c.getString(c.getColumnIndex("name"));
        s = new Person();
        s.setId(id);
        s.setName(name);
    }
    return s;
}

Anda dapat mengembalikan jenis apa pun dan menerima langsung suka. Tidak perlu mengetik typecast.

Person p = nextRow(cursor); // cursor is real database cursor.

Ini yang terbaik jika Anda ingin menyesuaikan jenis catatan lain daripada kursor nyata.


2
Anda mengatakan bahwa "tidak perlu typecast", tetapi jelas ada typecasting yang terlibat: (Cursor) cursormisalnya.
Sami Laine

Ini adalah penggunaan generik yang sama sekali tidak pantas. Kode ini cursorharus berupa Cursor, dan akan selalu mengembalikan Person(atau nol). Menggunakan obat generik menghilangkan pengecekan kendala ini, membuat kode tidak aman.
Andy Turner
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.