建立多種按鈕類型(JButton、JToggleButton、JCheckBox、JRadioButton)的互斥選擇群組,**僅 JRadioButton 與 JCheckBox (配合 ButtonGroup) 具備原生互斥行為**。本範例展示反射動態實例化,消除重複代碼。
public class ButtonGroups extends JPanel {
static String[] ids = {"June","Ward","Beaver","Wally","Eddie","Lumpy"};
// 通用面板工廠:傳入按鈕 Class 與標籤陣列
static JPanel makeBPanel(Class extends AbstractButton> btnClass, String[] labels) {
ButtonGroup group = new ButtonGroup();
JPanel panel = new JPanel();
panel.setBorder(new TitledBorder(
btnClass.getSimpleName() // 簡潔類名作標題
));
for (String label : labels) {
AbstractButton btn;
try {
// 反射獲取 String 參數建構子
Constructor extends AbstractButton> ctor =
btnClass.getConstructor(String.class);
btn = ctor.newInstance(label);
} catch (Exception ex) {
btn = new JButton("failed"); // 失敗降級
System.err.println("Can't create " + btnClass);
}
group.add(btn); // 加入群組(JButton 無互斥效果)
panel.add(btn);
}
return panel;
}
public ButtonGroups() {
add(makeBPanel(JButton.class, ids));
add(makeBPanel(JToggleButton.class, ids));
add(makeBPanel(JCheckBox.class, ids));
add(makeBPanel(JRadioButton.class, ids));
}
// main()...
}
btnClass.getConstructor(String.class):精確匹配單一 String 參數建構子。ctor.newInstance(label):動態創建實例,拋棄編譯期類型綁定。btnClass.getSimpleName():獲取無套裝名類名,作為邊框標題。| 按鈕類型 | 加入 ButtonGroup 後行為 |
|---|---|
| JRadioButton | **互斥選擇**:同群組僅一個可被選中 |
| JCheckBox | **互斥選擇**:同群組僅一個可被選中 (非標準用法,視覺上為方框) |
| JToggleButton | **無互斥**:各自獨立切換按下/彈起狀態 |
| JButton | **無互斥**:無選中狀態,單純點擊動作 |
// 型別安全工廠
static JPanel makePanel(
Supplier buttonFactory, String title, String... labels) {
ButtonGroup group = new ButtonGroup();
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(new TitledBorder(title));
for (String label : labels) {
T btn = buttonFactory.get();
btn.setText(label);
group.add(btn);
panel.add(btn);
}
return panel;
}
// 使用
add(makePanel(JRadioButton::new, "Radio", ids));
add(makePanel(() -> new JCheckBox(), "Check", ids));
反射動態建構按鈕群組展示了 **「配置驅動 UI」** 思想:按鈕類型與標籤資料分離,通用工廠方法消除樣板代碼。理解 ButtonGroup 僅對 JRadioButton/JCheckBox 生效的語義差異,避免誤用。現代 Java 建議改用函數式工廠,兼得型別安全與簡潔性。