【问题标题】:ComboBoxModel with Dynamic Proxied Objects具有动态代理对象的 ComboBoxModel
【发布时间】:2013-07-28 16:03:45
【问题描述】:

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

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

我留下一个例子,准备自己去看看。

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

}

嗯,就是这样!

谢谢!!

维克多。

【问题讨论】:

    标签: java swing comboboxmodel


    【解决方案1】:

    问题是,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) 更像@ 987654332@

    这实际上在Java Docs中注明

    对声明的 hashCode、equals 或 toString 方法的调用 代理实例上的 java.lang.Object 将被编码并分派到 调用处理程序的调用方法,方式与接口相同 如上所述,方法调用被编码和分派。这 声明传递给调用的方法对象的类将是 java.lang.对象。继承的代理实例的其他公共方法 来自 java.lang.Object 不会被代理类覆盖,所以 调用这些方法的行为就像它们对 java.lang.Object.

    我不知道你会如何解决这个问题

    【讨论】:

    • 非常完整的答案。所以,让我清楚一点......我们在这里面临的事实是 JComboBox 的代码可以将代理“A”与代理对象“B”进行比较,此外,代理“A”实际上是代理对象“B”,由于它们属于不同的类,equals 将失败..(equals 的任何合理实现都应该返回 false)。顺便说一句,我设法解决了一些问题.. 恢复代理对象(在 getSelectedItem 和 getElementAt 中),并拥有一个对象映射 - > 代理(为了检索代理作为键传递的对象的代理)
    • 是的,这是基本的。如果您尝试执行proxyA.equals(proxyB),它实际上并没有比较两个代理对象,而是通过将proxyA.objectBeginProxied 与永远无法返回trueproxyB 进行比较。这是一个有趣的变通方法,您可能希望通过变通方法更新您的问题,以供其他有相同问题的人使用
    猜你喜欢
    • 1970-01-01
    • 2012-06-10
    • 2010-10-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多