2013-07-29 28 views
2

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

我想获得一些更多的信息,但现在我只知道,当周围有代理对象的方法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); 
    } 

} 

那么,这就是全部!

谢谢!

Victor。

回答

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但它是代理方式的对象,这最终开始false(因为Proxy#equals(Proxy)更像Proxy.objectBeginProxied#equsl(Proxy)

这在实际上是注意被调用equals哈希码的Java Docs

的调用,等于或代理实例在 java.lang.Object中声明的toString方法将被编码,并分发给 以与接口 相同的方式调用处理程序的调用方法,如上所述对方法调用进行编码和调度。声明传递给调用的Method对象的类的 将是 java.lang.Object。来自java.lang.Object的代理实例继承的其他公共方法 不会被代理类覆盖,因此 这些方法的调用行为与它们对于 java.lang.Object实例的行为类似。

我不知道你将如何解决这个问题,虽然

+0

非常完成答案。所以,让我清楚的事情...我们在这里面临的事实是JComboBox的代码可以将代理“A”与代理对象“B”进行比较,此外,代理“A”实际上代表对象“B”,等于到它们属于不同类别的事实(任何合理的等价实现都应该返回false)。顺便说一句,我设法做一些变通.. returing被代理的对象(在getSelectedItem和getElementAt),并有一个对象 - >代理的映射(为了检索代理,将对象传递为键) – Victor

+0

是的,这是基本的。如果你尝试并执行'proxyA.equals(proxyB)',它实际上并没有比较两个代理对象,比较'proxyA.objectBeginProxied'和'proxyB',它永远不能返回'true'。这是一个有趣的工作,你可能想要更新你的问题与周围的工作,为任何其他人有同样的问题 – MadProgrammer