2013-07-29 39 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