java - 具有动态代理对象的 ComboBoxModel

标签 java swing comboboxmodel

据我们所知ComboBoxModel接口(interface)用于创建一个类,我们可以指定如何将对象集合(模型)与组合框相关联,基本上是通过提供有关如何检索项目和设置当前项目的必要“信息”。 一般来说,我编写了那些声明为成员 a Collection <of a concrete type> 的类。 ,并在实现的方法中将一些功能委托(delegate)给集合对象。 当所有包含对象的实际类都是非代理对象时,一切都会很好(肯定 90% 的情况都会遇到这种情况),但这次面对引用代理对象的事实,事情就会出现奇怪的错误。 JComboBox 行为出错,因为它无法更改当前选择。

我正在尝试获取更多信息,但现在我只知道方法 setSelectedItemComboBoxModel当周围有代理对象时,任何具体类实现的接口(interface)都不会被调用。这就是我的问题:发生了什么,更重要的是,它可以修复吗?

我留下一个例子,准备好亲自查看。

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 );
    }

}

好了,就这些了!

谢谢!!

维克多。

最佳答案

问题是,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;
    }
}

在设置对象之前验证该对象是否存在于模型中。

问题是,您的代理对象不是在 Proxy 上调用 equals,而是在它所代理的对象中调用 equals,这最终开始 false (因为 Proxy#equals(Proxy) 更像 Proxy.objectBeginProxied#equsl(Proxy)

这实际上在 Java Docs 中有说明。

An invocation of the hashCode, equals, or toString methods declared in java.lang.Object on a proxy instance will be encoded and dispatched to the invocation handler's invoke method in the same manner as interface method invocations are encoded and dispatched, as described above. The declaring class of the Method object passed to invoke will be java.lang.Object. Other public methods of a proxy instance inherited from java.lang.Object are not overridden by a proxy class, so invocations of those methods behave like they do for instances of java.lang.Object.

我不确定你会如何解决这个问题

关于java - 具有动态代理对象的 ComboBoxModel,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17914317/

相关文章:

java - 如何制作一个理想的缓存?

java - 将 Button 添加到 TableView 并将 Button 的 textProperty 绑定(bind)到 TableView 对象的属性

java - 消费者宕机时写一个kafka主题

java - Jackson:在将对象序列化为 json 时自定义转义双引号

java - 到达组件的澄清,Java Swing

java - 如何直接从数据库填充数据到Jtable

java - 关于如何调试 ComboBoxModel 的建议

java - 自动刷新jcombobox

java - JTable 禁用单元格中的复选框

java - 如何使用带有 Class 值的 JComboBox