我们知道ComboBoxModel
接口用于创建一个类,我们可以指定如何将对象集合(模型)与组合框相关联,基本上通过提供有关如何的必要“信息”检索项目并设置当前项目。
通常我写这些类声明为成员Collection <of a concrete type>
,并且只是在实现的方法中将一些功能委托给集合对象。
当所有包含的对象的实际类是一个Not-Proxied对象时,一切都很顺利(肯定有90%的时间我们都有这种情况),但是这次面对事实是引用了Proxied Objects并且事情发生了奇怪的错误。
JComboBox行为出错,因为它无法更改当前选择。
我正在尝试获取更多信息,但是现在我只知道setSelectedItem
接口的方法ComboBoxModel
,当有代理对象时,任何具体类实现的方法都不会调用。这是我的问题:发生了什么,更重要的是,它是否可以解决?
我留下一个例子,准备好自己看看。
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
/**
*
* @author Administrador
*/
public class AComboBoxWithProxyProblem extends JFrame implements ActionListener
{
ComboBoxModel modelWithProxies = new ItemComboBoxModelWithProxies();
ComboBoxModel modelWithoutProxies = new ItemComboBoxModelWithoutProxies();
public AComboBoxWithProxyProblem()
{
final JComboBox comboBox = new JComboBox();
comboBox.addActionListener( this );
getContentPane().setLayout(new BoxLayout(this.getContentPane(),BoxLayout.LINE_AXIS));
getContentPane().add(comboBox);
JRadioButton btnProxy = new JRadioButton("Proxy model");
btnProxy.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
comboBox.setModel(modelWithProxies);
comboBox.setSelectedIndex(0);
}
});
getContentPane().add(btnProxy);
JRadioButton btnNoProxy = new JRadioButton("Non Proxy model");
btnNoProxy.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e)
{
comboBox.setModel(modelWithoutProxies);
comboBox.setSelectedIndex(0);
}
});
getContentPane().add(btnNoProxy);
ButtonGroup group = new ButtonGroup();
group.add(btnProxy);
group.add(btnNoProxy);
setTitle("Mmmm...");
}
@Override
public void actionPerformed(ActionEvent e)
{
JComboBox comboBox = (JComboBox)e.getSource();
Item item = (Item)comboBox.getSelectedItem();
System.out.println("[actionPerformed] - " + item.getId() + " : " + item.getDescription() );
}
interface ItemInterface
{
String getDescription();
int getId();
@Override
String toString();
}
class Item implements AComboBoxWithProxyProblem.ItemInterface
{
private int id;
private String description;
public Item(int id, String description)
{
this.id = id;
this.description = description;
}
@Override
public int getId()
{
return id;
}
@Override
public String getDescription()
{
return description;
}
@Override
public String toString()
{
return description;
}
}
private class ItemComboBoxModelWithoutProxies extends AbstractListModel implements ComboBoxModel
{
List<ItemInterface> foos;
ItemInterface selected;
public ItemComboBoxModelWithoutProxies()
{
foos = new ArrayList<> ();
foos.add(new Item(1,"One"));
foos.add(new Item(2,"Two"));
foos.add(new Item(3,"Three"));
}
@Override
public Object getSelectedItem()
{
return selected;
}
@Override
public void setSelectedItem(Object tournament)
{
System.out.println("[setSelectedItem] " + tournament);
selected = (ItemInterface) tournament;
}
@Override
public int getSize()
{
return this.foos.size();
}
@Override
public Object getElementAt(int i)
{
return this.foos.get(i);
}
}
private class ItemComboBoxModelWithProxies extends AbstractListModel implements ComboBoxModel
{
List<ItemInterface> foos;
Object selected;
public ItemComboBoxModelWithProxies()
{
foos = new ArrayList<> ();
ItemInterface item;
item = (ItemInterface) Proxy.newProxyInstance(Item.class.getClassLoader(),
Item.class.getInterfaces(),
new ItemInvocationHandler (new Item(1,"One")));
foos.add(item);
item = (ItemInterface) Proxy.newProxyInstance(Item.class.getClassLoader(),
Item.class.getInterfaces(),
new ItemInvocationHandler (new Item(2,"Two")));
foos.add(item);
item = (ItemInterface) Proxy.newProxyInstance(Item.class.getClassLoader(),
Item.class.getInterfaces(),
new ItemInvocationHandler (new Item(3,"Three")));
foos.add(item);
}
@Override
public Object getSelectedItem()
{
return selected;
}
@Override
public void setSelectedItem(Object tournament)
{
System.out.println("[setSelectedItem] " + tournament);
selected = (ItemInterface) tournament;
}
@Override
public int getSize()
{
return this.foos.size();
}
@Override
public Object getElementAt(int i)
{
return this.foos.get(i);
}
private class ItemInvocationHandler implements InvocationHandler {
Item item;
public ItemInvocationHandler(Item item)
{
this.item = item;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
return method.invoke(this.item, args);
}
}
}
public static void main(String[] args)
{
JFrame frame = new AComboBoxWithProxyProblem();
frame.setDefaultCloseOperation( EXIT_ON_CLOSE );
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible( true );
}
}
嗯,就是这样!
谢谢!
胜者。
答案 0 :(得分:4)
问题是,JComboBox
正在使用Object#equals
来比较值。
JComboBox#setSelectedIndex
正在调用JComboBox#getSelectedItem
,正在使用...
for (int i = 0; i < dataModel.getSize(); i++) {
E element = dataModel.getElementAt(i);
if (anObject.equals(element)) {
found = true;
objectToSelect = element;
break;
}
}
在设置之前验证对象是否存在于模型中。
问题是,您的代理对象正在调用equals
而不是Proxy
,而是在它所代理的对象中调用equals
,最终开始false
(因为Proxy#equals(Proxy)
更像Proxy.objectBeginProxied#equsl(Proxy)
实际上已在Java Docs
中注明调用在其中声明的hashCode,equals或toString方法 代理实例上的java.lang.Object将被编码并分派到 调用处理程序的调用方法与接口的方式相同 如上所述,对方法调用进行编码和分派。该 声明传递给invoke的Method对象的类将是 java.lang.Object继承。继承了代理实例的其他公共方法 来自java.lang.Object的内容不会被代理类覆盖,所以 这些方法的调用就像它们的实例一样 java.lang.Object中。
我不知道你怎么解决这个问题