问题
在使用Scala实现JTV客户端界面程序时,我遇到了Scala重载Java类中的泛型方法的问题。
因为界面上的JList
使用了自定义的元素类型,我需要自定义ListCellRender
来列表对象中元素行的显示。最简单的方法就是直接继承DefaultListCellRenderer
,它会将JList
中的数据元素渲染为JLabel
,我只需要覆盖其getListCellRendererComponent
实现元素转JLabel
的逻辑即可。
自定义类的结构如下:
1
2
3
|
class FileRender extends DefaultListCellRenderer{
override def getListCellRendererComponent(list: JList[_], value: scala.Any, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
}
|
编译时会产生错误:
1
2
|
Error:(441, 9) class FileRender needs to be abstract, since method getListCellRendererComponent in trait ListCellRenderer of type (x$1: javax.swing.JList[_ <: Object], x$2: Object, x$3: Int, x$4: Boolean, x$5: Boolean)java.awt.Component is not defined
class FileRender extends DefaultListCellRenderer{
|
原因分析
查看DefaultListCellRender
和ListCellRender
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
public class DefaultListCellRenderer extends JLabel
implements ListCellRenderer<Object>, Serializable
{
public Component getListCellRendererComponent(
JList<?> list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus)
{
...
}
...
}
public interface ListCellRenderer<E>
{
Component getListCellRendererComponent(
JList<? extends E> list,
E value,
int index,
boolean isSelected,
boolean cellHasFocus);
}
|
从错误信息中可以看到,错误的原因在于我们的实现与ListCellRender
中方法的泛型参数不匹配。先把第一个参数的泛型参数按错误提示进行修正:
1
2
3
|
class FileRender extends DefaultListCellRenderer{
override def getListCellRendererComponent(list: JList[_ <: Object], value: scala.Any, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus)
}
|
再编译时,编译器会提示getListCellRendererComponent
未覆盖任何方法。
从错误信息中可以了解到,应该是由于Scala对于Java实现对接口中的泛型参数无法理解。而我们编写的Scala继承Java类之后,Scala编译器不认为Java实现类与接口中的两个方法具有相同的方法签名。无论我们按接口的签名编写,还是按Java实现类的编译都会导致编译失败。
解决
在网上搜索这个问题找到了几篇有价值的贴子:
类似问题
Martin Odersky对这类问题的回复
在这个贴子中,Martin Odersky对这类问题的建议是用Java编写一个实现类,之后再用Scala继承。
同一个问题
而针对我们这个具体的问题,上面这个篇贴子给出的方法更简单。它直接用Scala编写ListCellRender
的实现,用它作为DefaultListCellRender
的代理类。
1
2
3
4
5
6
7
8
9
10
11
|
class FileRender extends ListCellRenderer[FileInfo]{
val render = (new DefaultListCellRenderer).asInstanceOf[ListCellRenderer[FileInfo]]
override def getListCellRendererComponent(list: JList[_ <: FileInfo], value: FileInfo, index: Int, isSelected: Boolean, cellHasFocus: Boolean): Component = {
val result = render.getListCellRendererComponent(list,value,index,isSelected,cellHasFocus)
val label = result.asInstanceOf[JLabel]
label.setText(value.file.getName)
label.setIcon(ImageUtils.toImageIcon(value.icon))
label
}
}
|