Generics 通过协议传递类型参数?
我正在尝试使用泛型实现以下用例的解决方案: 有一个dialog类,dialog用于从列表中选择项目。对话框必须是通用的,因此我为项目创建了一个协议,该协议提供了列表中使用的标签,并为代理创建了一个协议,用于处理单击的项目 对话框将单击的项目传递给代理。因为我使用的是泛型,所以我的想法是,为了使项目更准确地包装在具有正确类型的项目中的模型对象,我不必使用铸造。我在一个游乐场文件中准备了一个自包含的示例来说明我的意思 这不会编译,因为我没有向下转换该项 我可以用Java解决这个问题-刚刚实现它,DialogListener和DialogItem有类型参数,因此我可以传递int声明/实例化,在侦听器中我接收参数DialogItem,因此不需要向下转换 我怀疑可能无法在Swift中执行此操作,因为我在文档中没有看到任何可用的内容,但我希望我错了 我想明确一点,问题是,作为一个通用的解决方案,我如何实现这一点,而不必使用任何强制转换Generics 通过协议传递类型参数?,generics,swift,Generics,Swift,我正在尝试使用泛型实现以下用例的解决方案: 有一个dialog类,dialog用于从列表中选择项目。对话框必须是通用的,因此我为项目创建了一个协议,该协议提供了列表中使用的标签,并为代理创建了一个协议,用于处理单击的项目 对话框将单击的项目传递给代理。因为我使用的是泛型,所以我的想法是,为了使项目更准确地包装在具有正确类型的项目中的模型对象,我不必使用铸造。我在一个游乐场文件中准备了一个自包含的示例来说明我的意思 这不会编译,因为我没有向下转换该项 我可以用Java解决这个问题-刚刚实现它,Di
///////////////////////////////////////////////////////
// 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的泛型,但这似乎非常有限。