Generics 通过协议传递类型参数?

Generics 通过协议传递类型参数?,generics,swift,Generics,Swift,我正在尝试使用泛型实现以下用例的解决方案: 有一个dialog类,dialog用于从列表中选择项目。对话框必须是通用的,因此我为项目创建了一个协议,该协议提供了列表中使用的标签,并为代理创建了一个协议,用于处理单击的项目 对话框将单击的项目传递给代理。因为我使用的是泛型,所以我的想法是,为了使项目更准确地包装在具有正确类型的项目中的模型对象,我不必使用铸造。我在一个游乐场文件中准备了一个自包含的示例来说明我的意思 这不会编译,因为我没有向下转换该项 我可以用Java解决这个问题-刚刚实现它,Di

我正在尝试使用泛型实现以下用例的解决方案:

有一个dialog类,dialog用于从列表中选择项目。对话框必须是通用的,因此我为项目创建了一个协议,该协议提供了列表中使用的标签,并为代理创建了一个协议,用于处理单击的项目

对话框将单击的项目传递给代理。因为我使用的是泛型,所以我的想法是,为了使项目更准确地包装在具有正确类型的项目中的模型对象,我不必使用铸造。我在一个游乐场文件中准备了一个自包含的示例来说明我的意思

这不会编译,因为我没有向下转换该项

我可以用Java解决这个问题-刚刚实现它,DialogListener和DialogItem有类型参数,因此我可以传递int声明/实例化,在侦听器中我接收参数DialogItem,因此不需要向下转换

我怀疑可能无法在Swift中执行此操作,因为我在文档中没有看到任何可用的内容,但我希望我错了

我想明确一点,问题是,作为一个通用的解决方案,我如何实现这一点,而不必使用任何强制转换

///////////////////////////////////////////////////////
// Model class

class Student {
    let name:String

    init(name:String) {
        self.name = name
    }
}

///////////////////////////////////////////////////////
// Dialog item, this wraps the model class

protocol DialogItem {

    typealias T

    func getLabel() -> String

    func getModel() -> T
}

///////////////////////////////////////////////////////
// Implementation of dialog item "wrapper" for student

class StudentDialogItem : DialogItem {

    let student:Student

    init(student:Student) {
        self.student = student
    }

    func getLabel() -> String {
        return student.name
    }

    func getModel() -> Student {
        return student
    }
}


///////////////////////////////////////////////////////
// Listener
// ---> how can I pass T to DialogListener and then to DialogItem, to get items with correct type?

protocol DialogListener {


    func onItemSelected(item:DialogItem)
}


///////////////////////////////////////////////////////
// Dialog, with "dummy" functionality

class ItemSelectionDialog {
    let items:[DialogItem]
    let listener:DialogListener

    init (items:[DialogItem], listener:DialogListener) {
        self.items = items
        self.listener = listener
    }

    func show() {
        for item in items {
            println(item.getLabel())
        }
    }

    func simulateClick(index:Int) {
        listener.onItemSelected(items[index])
    }
}

///////////////////////////////////////////////////////
// Implementation of dialog listener

class DialogListenerImpl : DialogListener {

    func onItemSelected(item: DialogItem)  {
        let student:Student = item.getModel() //<---- DialogItem doesn't have type information, compiler doesn't even seem to recognise getModel()!
        //do something with student
        println("Selected a student!, name: " + student.name)
    }
}



///////////////////////////////////////////////////////

let items:[Student] = [Student(name: "Student1-name"), Student(name: "Student2-name"), Student(name: "Student3-name")]

let listener = DialogListenerImpl()

let studentDialogItems:[StudentDialogItem] = items.map({StudentDialogItem(student: $0)})

let dialog = ItemSelectionDialog(items: studentDialogItems, listener: listener)

dialog.show()

dialog.simulateClick(1)
这是Java代码,可以按照我的要求工作:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

///////////////////////////////////////////////////////
//Model class

class Student {

    private String name;

    public Student(String name) {
        this.name = name;
    }   

    public String getName() {
        return name;
    }
}

///////////////////////////////////////////////////////
//Dialog item, this wraps the model class

class StudentDialogItem implements DialogItem<Student> {

    private Student student;

    public StudentDialogItem(Student student) {
        this.student = student;
    }

    @Override
    public String getLabel() {
        return student.getName();
    }

    @Override
    public Student getModel() {
        return student;
    }
}

///////////////////////////////////////////////////////
//Implementation of dialog item "wrapper" for student

interface DialogItem<T> {

    String getLabel();

    T getModel();   
}


///////////////////////////////////////////////////////
//Listener

interface DialogListener<T> {

    void onItemSelected(DialogItem<T> item);    
}

///////////////////////////////////////////////////////
//Dialog, with "dummy" functionality

class ItemSelectionDialog<T> {

    private List<DialogItem<T>> items;
    private DialogListener<T> listener;

    public ItemSelectionDialog(List<DialogItem<T>> items, DialogListener<T> listener) {
        this.items = items;
        this.listener = listener; 
    }

    public void show() {
        for (DialogItem<T> item : items) {
            System.out.println(item.getLabel());
        }
    }

    //Simulate click
    public void simulateClick(int index) {

        listener.onItemSelected(items.get(index));
    }
}

///////////////////////////////////////////////////////
//Implementation of dialog listener

class DialogListenerImpl implements DialogListener<Student> {

    @Override
    public void onItemSelected(DialogItem<Student> item) {
        Student student = item.getModel();              
        //do something with student
        System.out.println("Selected a student!, name: " + student.getName());
    }
}

///////////////////////////////////////////////////////

public class Main {

    public static void main(String[] args) {
        List<Student> students = Arrays.asList(new Student("Student1-name"), new Student("Student2-name"), new Student("Student3-name"));

        DialogListener<Student> listener = new DialogListenerImpl();

        //Wrap the model objects in dialog items
        List<DialogItem<Student>> studentDialogItems = new ArrayList<DialogItem<Student>>();
        for (Student student : students) {
            studentDialogItems.add(new StudentDialogItem(student));
        }

        ItemSelectionDialog<Student> dialog = new ItemSelectionDialog<Student>(studentDialogItems, listener);

        dialog.show();

        dialog.simulateClick(1);
    }
}

不支持协议中的泛型类型。但是item.getModel返回Student的事实在onSelectedItem实现中的Java和Swift代码中都是已知的。在Java代码中,可以将其作为泛型类型系统的一部分声明为onItemSelectedDialogItem项。在Swift代码中,您可以使用:

let student:Student = item.getModel() as Student

这并不令人满意,但很有效。

好吧,我明确要求一个解决方案,而不是强制转换。泛型的要点是在编译时进行检查。对于Java版本,如果我在DialogListenerImpl中使用了错误的类型,编译器会抱怨。如果我使用了错误的类型,它将在运行时崩溃。但请注意,存在一致性。您将DialogItem和DialogItemListener定义为Swift代码的通用部分,然后将Student、StudentDialogItem和StudentDialogItemListener(您称之为DialogListenerImpl)定义为特定类型部分。是的,但正如您所说,这并不令人满意。我从编程指南中了解到,协议支持使用typealias的泛型,但这似乎非常有限。