• 追加された行はこの色です。
  • 削除された行はこの色です。
---
keywords: [Kotlin, Swing, Java]
description: KotlinでSwingコンポーネントを使用するサンプルと、Javaからの変換に関するメモなど
author: aterai
pubdate: 2017-05-31
---
#contents

* 概要 [#summary]
`Kotlin`で`Swing`コンポーネントを使用するサンプルと、`Java`からの変換に関するメモです。

* 実行環境 [#s42e991c]
- [https://sdkman.io/ SDKMAN! the Software Development Kit Manager]で`kotlin`をインストール可能

 $ curl -s "https://get.sdkman.io" | bash 
 $ sdk install kotlin
 $ kotlinc -version
 info: kotlinc-jvm 1.1.4-3 (JRE 1.8.0_141-b15)
 $ kotlinc hello.kt -include-runtime -d hello.jar && "$JAVA_HOME/bin/java" -jar hello.jar

* Swing + Kotlin サンプル [#swing]
** JTable [#JTable]
- `Class<?>`は%%`Class<Any>`か`Class<Object>`?%%、`Class<*>`か`Class<out Any>`に変換した方が良いかもしれない
-- `IntelliJ`の自動変換では`Class<*>`
- `Object#getClass()`は`o.javaClass`
-- [https://kotlinlang.org/docs/reference/java-interop.html#getclass getClass() - Calling Java from Kotlin - Kotlin Programming Language]
-- `Integer::class.java`は`Class<Integer>`になるので、以下のサンプルで使用すると、`error: type inference failed. Expected type mismatch: inferred type is Class<Integer> but Class<Any> was expected`とエラーになる
-- [https://kotlinlang.org/docs/reference/generics.html#star-projections Star-projections - Generics - Kotlin Programming Language]
- オーバーライドの方法、`@Override`は`override`
- 配列、二次元配列
-- [https://kotlinlang.org/docs/reference/java-interop.html#java-arrays Java Arrays - Calling Java from Kotlin - Kotlin Programming Language]
-- `IntelliJ`の自動変換では`arrayOf(arrayOf<Any>("aaa", 12, true), ...`
- `apply`
- `switch`は`when`
- `DefaultTableModel#getColumnClass(...)`メソッドをオーバーライドして、`Boolean::class.java`を返してもセルレンダラーに`JCheckBox`は適用されない
-- `IntelliJ`の自動変換では`Boolean::class.java`になるが、`java.lang.Boolean::class.java`に修正する必要がある

#twocolumn
`Java`
#code{{
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;

public class JTableExample {
  public JComponent makeUI() {
    String[] cn = {"String", "Integer", "Boolean"};
    Object[][] data = {
      {"aaa", 12, true}, {"bbb", 5, false},
      {"CCC", 92, true}, {"DDD", 0, false}
    };
    TableModel m = new DefaultTableModel(data, cn) {
      @Override public Class<?> getColumnClass(int col) {
        return getValueAt(0, col).getClass();
      }
    };
    JTable table = new JTable(m);
    table.setAutoCreateRowSorter(true);

    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    return p;
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      f.add(new JTableExample().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}
}}
#twocolumn
`Kotlin`
#code(lang-kotlin){{
import java.awt.*
import javax.swing.*
import javax.swing.table.*

fun makeUI(): JComponent {
  val cn = arrayOf("String", "Integer", "Boolean")
  val data = arrayOf(
    arrayOf("aaa", 12, true), arrayOf("bbb", 5, false),
    arrayOf("CCC", 92, true), arrayOf("DDD", 0, false))
  val m = object: DefaultTableModel(data, cn) {
    override fun getColumnClass(col: Int): Class<Any> {
      return getValueAt(0, col).javaClass
    }
  }
  // val m = object: DefaultTableModel(data, cn) {
  //   override fun getColumnClass(col: Int): Class<*> {
  //     return when (col) {
  //       0 -> String::class.java
  //       1 -> Integer::class.java
  //       // XXX: 2 -> Boolean::class.java
  //       2 -> java.lang.Boolean::class.java
  //       else -> Object::class.java
  //     }
  //   }
  // }
  val table = JTable(m).apply {
    autoCreateRowSorter = true
  }
  return JPanel(BorderLayout(5, 5)).apply {
    add(JScrollPane(table))
  }
}
fun main(args: Array<String>) {
  EventQueue.invokeLater {
    JFrame("kotlin swing").apply {
      defaultCloseOperation = JFrame.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }
  }
}
}}
#twocolumn

** JTree [#JTree]
#twocolumn
`Java`
#code{{
import java.awt.*;
import java.util.*;
import javax.swing.*;

public class JTreeExample {
  public JComponent makeUI() {
    JTree tree = new JTree();

    JButton b1 = new JButton("expand");
    b1.addActionListener(e -> expandAll(tree));

    JButton b2 = new JButton("collapse");
    b2.addActionListener(e -> collapseAll(tree));

    JPanel pp = new JPanel(new GridLayout(1, 0, 5, 5));
    Arrays.asList(b1, b2).forEach(pp::add);
    JPanel p = new JPanel(new BorderLayout(5, 5));
    p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    p.add(new JScrollPane(tree));
    p.add(pp, BorderLayout.SOUTH);
    return p;
  }
  protected static void expandAll(JTree tree) {
    int row = 0;
    while (row < tree.getRowCount()) {
      tree.expandRow(row);
      row++;
    }
  }
  protected static void collapseAll(JTree tree) {
    int row = tree.getRowCount() - 1;
    while (row >= 0) {
      tree.collapseRow(row);
      row--;
    }
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      f.add(new JTreeExample().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}
}}
#twocolumn
`Kotlin`
#code(lang-kotlin){{
import java.awt.*
import javax.swing.*

fun makeUI(): JComponent {
  val tree = JTree()
  val p = JPanel(GridLayout(1, 0, 5, 5)).apply {
    add(JButton("expand").apply {
      addActionListener { expandAll(tree) }
    })
    add(JButton("collapse").apply {
      addActionListener { collapseAll(tree) }
    })
  }
  return JPanel(BorderLayout(5, 5)).apply {
    setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5))
    add(JScrollPane(tree))
    add(p, BorderLayout.SOUTH)
  }
}
fun expandAll(tree: JTree) {
  var row = 0
  while (row < tree.getRowCount()) {
    tree.expandRow(row)
    row++
  }
}
fun collapseAll(tree : JTree) {
  var row = tree.getRowCount() - 1
  while (row >= 0) {
    tree.collapseRow(row)
    row--
  }
}
fun main(args: Array<String>) {
  EventQueue.invokeLater {
    JFrame("kotlin swing").apply {
      defaultCloseOperation = JFrame.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }
  }
}
}}
#twocolumn

** JCheckBox [#JCheckBox]
- `Smart Cast`、キャストで`if`のネストが深くなるのを避けたい
-- イベント発生元が`AbstractButton`なのは自明なので`as`を使用する
--- `val b = e.getSource() as AbstractButton`
-- 以下のような場合は、`apply`でも回避可能

#twocolumn
`Java`
#code{{
import java.awt.*;
import javax.swing.*;

public class JCheckBoxExample {
  public JComponent makeUI() {
    JCheckBox cb = new JCheckBox("Always On Top", true);
    cb.addActionListener(e -> {
      AbstractButton b = (AbstractButton) e.getSource();
      Container c = b.getTopLevelAncestor();
      if (c instanceof Window) {
        ((Window) c).setAlwaysOnTop(b.isSelected());
      }
    });
    JPanel p = new JPanel(new BorderLayout());
    p.add(cb, BorderLayout.NORTH);
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      f.add(new JCheckBoxExample().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}
}}
#twocolumn
`Kotlin`
#code(lang-kotlin){{
import java.awt.*
import javax.swing.*

fun makeUI(): JComponent {
  val cb = JCheckBox("Always On Top", true).apply {
//     addActionListener { e ->
//       val c = e.getSource()
//       if (c is AbstractButton) {
//         val w = c.getTopLevelAncestor()
//         if (w is Window) {
//           w.setAlwaysOnTop(c.isSelected())
//         }
//       }
    addActionListener { e ->
      val b = e.getSource() as AbstractButton
      val w = b.getTopLevelAncestor()
      if (w is Window) {
        w.setAlwaysOnTop(b.isSelected())
      }
    }
//     addActionListener {
//       val w = getTopLevelAncestor()
//       if (w is Window) {
//           w.setAlwaysOnTop(isSelected())
//       }
//     }
  }
  return JPanel(BorderLayout()).apply {
    add(cb, BorderLayout.NORTH)
  }
}

fun main(args: Array<String>) {
  EventQueue.invokeLater {
    JFrame("kotlin swing").apply {
      defaultCloseOperation = JFrame.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }
  }
}
}}
#twocolumn

** インターフェイス、匿名内部クラス、throws [#tb58a862]
- `IntelliJ`の自動変換では`error: this class does not have a constructor tree.addTreeWillExpandListener(object : TreeWillExpandListener() {`とエラーになる
- インターフェイスから匿名内部クラスのインスタンスを生成する場合は`()`は不要
-- [https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SAM Conversions - Calling Java from Kotlin - Kotlin Programming Language]
-- `tree.addTreeWillExpandListener(object : TreeWillExpandListener() {`を`tree.addTreeWillExpandListener(object : TreeWillExpandListener {`に修正

#twocolumn
`Java`
#code{{
tree.addTreeWillExpandListener(new TreeWillExpandListener() {
  @Override public void treeWillExpand(TreeExpansionEvent e)
      throws ExpandVetoException {
    //throw new ExpandVetoException(e, "Tree expansion cancelled");
  }
  @Override public void treeWillCollapse(TreeExpansionEvent e)
      throws ExpandVetoException {
    throw new ExpandVetoException(e, "Tree collapse cancelled");
  }
});
}}

#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
tree.addTreeWillExpandListener(object : TreeWillExpandListener() {
  @Override
  @Throws(ExpandVetoException::class)
  fun treeWillExpand(e: TreeExpansionEvent) {
    //throw new ExpandVetoException(e, "Tree expansion cancelled");
  }
  @Override
  @Throws(ExpandVetoException::class)
  fun treeWillCollapse(e: TreeExpansionEvent) {
    throw ExpandVetoException(e, "Tree collapse cancelled")
  }
})
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
tree.addTreeWillExpandListener(object : TreeWillExpandListener {
  @Throws(ExpandVetoException::class)
  override fun treeWillExpand(e: TreeExpansionEvent) {
    //throw new ExpandVetoException(e, "Tree expansion cancelled");
  }
  @Throws(ExpandVetoException::class)
  override fun treeWillCollapse(e: TreeExpansionEvent) {
    throw ExpandVetoException(e, "Tree collapse cancelled")
  }
})
}}
#twocolumn

#twocolumn
`Java`
#code{{
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.IconUIResource;
import javax.swing.tree.*;

public class ThrowsExample {
  public JComponent makeUI() {
    JPanel p = new JPanel(new GridLayout(1, 0, 5, 5));
    p.add(new JScrollPane(new JTree()));
    p.add(new JScrollPane(makeTree()));
    p.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
    return p;
  }
  private static JTree makeTree() {
    Icon icon = new EmptyIcon();
    UIManager.put("Tree.expandedIcon",  new IconUIResource(icon));
    UIManager.put("Tree.collapsedIcon", new IconUIResource(icon));

    JTree tree = new JTree();
    tree.setEditable(true);
    tree.setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4));
    int row = 0;
    while (row < tree.getRowCount()) {
      tree.expandRow(row++);
    }
    tree.addTreeWillExpandListener(new TreeWillExpandListener() {
      @Override public void treeWillExpand(TreeExpansionEvent e)
          throws ExpandVetoException {
        //throw new ExpandVetoException(e, "Tree expansion cancelled");
      }
      @Override public void treeWillCollapse(TreeExpansionEvent e)
          throws ExpandVetoException {
        throw new ExpandVetoException(e, "Tree collapse cancelled");
      }
    });
    return tree;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new ThrowsExample().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class EmptyIcon implements Icon {
  @Override public void paintIcon(Component c, Graphics g, int x, int y) {
    /* Empty icon */
  }
  @Override public int getIconWidth() {
    return 0;
  }
  @Override public int getIconHeight() {
    return 0;
  }
}
}}
#twocolumn
`Kotlin`
#code(lang-kotlin){{
import java.awt.*
import javax.swing.*
import javax.swing.event.*
import javax.swing.plaf.IconUIResource
import javax.swing.tree.*

private fun makeUI(): Component = JPanel(GridLayout(1, 0, 5, 5)).apply {
  setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5))
  add(JScrollPane(JTree()))
  add(JScrollPane(makeTree()))
}

private fun makeTree(): JTree = JTree().apply {
  setEditable(true)
  setBorder(BorderFactory.createEmptyBorder(4, 4, 4, 4))
  var row = 0
  while (row < getRowCount()) {
    expandRow(row++)
  }
  addTreeWillExpandListener(object : TreeWillExpandListener {
    @Throws(ExpandVetoException::class)
    override fun treeWillExpand(e: TreeExpansionEvent) {
      //throw new ExpandVetoException(e, "Tree expansion cancelled");
    }
    @Throws(ExpandVetoException::class)
    override fun treeWillCollapse(e: TreeExpansionEvent) {
      throw ExpandVetoException(e, "Tree collapse cancelled")
    }
  })
}

fun main(args: Array<String>) {
  EventQueue.invokeLater {
    UIManager.put("swing.boldMetal", false)
    val icon = EmptyIcon()
    UIManager.put("Tree.expandedIcon", IconUIResource(icon))
    UIManager.put("Tree.collapsedIcon", IconUIResource(icon))
    JFrame("kotlin swing").apply {
      defaultCloseOperation = JFrame.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }
  }
}

internal class EmptyIcon : Icon {
  override fun getIconWidth() : Int = 0
  override fun getIconHeight() : Int = 0
  override fun paintIcon(c: Component, g: Graphics, x: Int, y: Int) {
    /* Empty icon */
  }
}
}}
#twocolumn

** Optional [#Optional]
- `Optional#orElse(...)`は、`let {...} ?: ...`に変換
-- `Integer stepSize = Optional.ofNullable(stepSizeMap.get(calendarField)).orElse(1);`
-- `val stepSize = stepSizeMap.get(calendarField).let { it } ?: 1`
- 例えば`SpinnerDateModel#getNextValue(...)`メソッドを`override fun getNextValue(): Object = Calendar.getInstance().apply { ...`のように変換すると、`error: type mismatch: inferred type is Date! but Object was expected`とエラーになる
- `override fun getNextValue(): Any = Calendar.getInstance().apply { ...`と`Any`を使用するようオーバーライドする必要がある
- `Date!`は、`Date`もしくは`Date?`の意味
-- [http://kotlinlang.org/docs/reference/java-interop.html#null-safety-and-platform-types Calling Java from Kotlin - Kotlin Programming Language]
-- `T! means "T or T?"`

#twocolumn
`Java`
#code{{
import java.awt.*;
import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.text.*;

public class OptionalExample {
  private JComponent makeUI() {
    SimpleDateFormat format = new SimpleDateFormat(
        "mm:ss, SSS", Locale.getDefault());
    DefaultFormatterFactory factory = new DefaultFormatterFactory(
        new DateFormatter(format));

    Calendar calendar = Calendar.getInstance();
    calendar.set(Calendar.HOUR_OF_DAY, 0);
    calendar.clear(Calendar.MINUTE);
    calendar.clear(Calendar.SECOND);
    calendar.clear(Calendar.MILLISECOND);
    Date d = calendar.getTime();

    JSpinner sp1 = new JSpinner(new SpinnerDateModel(
        d, null, null, Calendar.SECOND));
    JSpinner.DefaultEditor ed1 = (JSpinner.DefaultEditor) sp1.getEditor();
    ed1.getTextField().setFormatterFactory(factory);

    HashMap<Integer, Integer> stepSizeMap = new HashMap<>();
    stepSizeMap.put(Calendar.HOUR_OF_DAY, 1);
    stepSizeMap.put(Calendar.MINUTE,      1);
    stepSizeMap.put(Calendar.SECOND,      30);
    stepSizeMap.put(Calendar.MILLISECOND, 500);

    JSpinner sp2 = new JSpinner(new SpinnerDateModel(
        d, null, null, Calendar.SECOND) {
      @Override public Object getPreviousValue() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getDate());
        Integer calendarField = getCalendarField();
        Integer stepSize = Optional.ofNullable(
            stepSizeMap.get(calendarField)).orElse(1);
        cal.add(calendarField, -stepSize);
        return cal.getTime();
      }
      @Override public Object getNextValue() {
        Calendar cal = Calendar.getInstance();
        cal.setTime(getDate());
        Integer calendarField = getCalendarField();
        Integer stepSize = Optional.ofNullable(
            stepSizeMap.get(calendarField)).orElse(1);
        cal.add(calendarField, stepSize);
        return cal.getTime();
      }
    });
    JSpinner.DefaultEditor ed2 = (JSpinner.DefaultEditor) sp2.getEditor();
    ed2.getTextField().setFormatterFactory(factory);

    JPanel p = new JPanel();
    p.add(sp1);
    p.add(sp2);
    return p;
  }
  public static void main(String... args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new OptionalExample().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}
}}
#twocolumn
`Kotlin`
#code(lang-kotlin){{
import java.awt.*
import java.text.*
import java.util.*
import javax.swing.*
import javax.swing.text.*

fun makeUI(): JComponent {
  val format = SimpleDateFormat("mm:ss, SSS", Locale.getDefault())
  val factory = DefaultFormatterFactory(DateFormatter(format))

  val calendar = Calendar.getInstance().apply {
    set(Calendar.HOUR_OF_DAY, 0)
    clear(Calendar.MINUTE)
    clear(Calendar.SECOND)
    clear(Calendar.MILLISECOND)
  }
  val d = calendar.getTime()

  val sp1 = JSpinner(SpinnerDateModel(d, null, null, Calendar.SECOND))
  val ed1 = sp1.getEditor()
  if (ed1 is JSpinner.DefaultEditor) {
    ed1.getTextField().setFormatterFactory(factory)
  }

  val stepSizeMap: HashMap<Int, Int> = hashMapOf(
      Calendar.HOUR_OF_DAY to 1,
      Calendar.MINUTE      to 1,
      Calendar.SECOND      to 30,
      Calendar.MILLISECOND to 500)

  val sp2 = JSpinner(object: SpinnerDateModel(d, null, null, Calendar.SECOND) {
    override fun getPreviousValue(): Any = Calendar.getInstance().apply {
      time = getDate()
      val stepSize = stepSizeMap.get(calendarField).let { it } ?: 1
      add(calendarField, -stepSize)
    }.getTime()
    override fun getNextValue(): Any = Calendar.getInstance().apply {
        setTime(getDate())
        val stepSize = stepSizeMap.get(calendarField).let { it } ?: 1
        add(calendarField, stepSize)
    }.getTime()
  })
  val ed2 = sp2.getEditor()
  if (ed2 is JSpinner.DefaultEditor) {
    ed2.getTextField().setFormatterFactory(factory)
  }

  return JPanel().apply {
    add(sp1)
    add(sp2)
  }
}

fun main(args: Array<String>) {
  EventQueue.invokeLater {
    JFrame("kotlin swing").apply {
      defaultCloseOperation = WindowConstants.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }
  }
}
}}
#twocolumn

** 演算子の優先順位[#precedence]
- 論理演算子: 左シフト
-- `Java`: `buttons[0].setSelected((i & (1 << 2) ) != 0);`
-- `Kotlin`: `buttons[0].setSelected(i and (1 shl 2) != 0)`
--- `IntelliJ`の自動変換では括弧が減る(`and`の優先順位が異なる?)
--- `IntelliJ`の自動変換では時々`buttons[0].setSelected(i and (1 shl 2 != 0) )`に誤変換される?
- `bitwise AND`(`and`の優先順位)
-- [https://discuss.kotlinlang.org/t/bitwise-operators-are-not-in-the-precedence-table/2075 Bitwise operators are not in the precedence table - Support - Kotlin Discussions]
-- [https://kotlinlang.org/docs/reference/grammar.html#precedence Kotlinの演算子優先順位表]
--- [https://kotlinlang.org/docs/reference/basic-types.html#operations Basic Types: Numbers, Strings, Arrays - Kotlin Programming Language]
--- `Kotlin`の`bitwise and`は`infix function`のため、`Equality`の`!=`より優先順位が高い
-- `Java`の演算子優先順位表: [https://docs.oracle.com/javase/tutorial/java/nutsandbolts/operators.html Operators (The Java™ Tutorials > Learning the Java Language > Language Basics)]
--- `Java`は逆で、`equality`(`==`, `!=`)のほうが`bitwise AND`(`&`)より優先順位が高い

----
- `extends`, `implements`
-- `IntelliJ`の自動変換では勝手に`open`は付けないようなので、以下のようなエラーが出る場合は、`internal class CheckBoxesPanel : JPanel() {`を`open class CheckBoxesPanel : JPanel() {`に修正する
#code{{
MainPanel.kt:81:37: error: this type is final, so it cannot be inherited from
internal class CheckBoxesRenderer : CheckBoxesPanel(), TableCellRenderer {
}}
- `IntelliJ`の自動変換では`static`変数がうまく変換できない?
-- 手動で`enum class`にして回避
#code(lang-kotlin){{
companion object {
  protected val TITLES = arrayOf("r", "w", "x")
}
//...
MainPanel.kt:45:5: error: property must be initialized or be abstract
    var buttons: Array<JCheckBox>
    ^
MainPanel.kt:55:19: error: type mismatch: inferred type is Array<JCheckBox?> but Array<JCheckBox> was expected
        buttons = arrayOfNulls<JCheckBox>(TITLES.size)
}}

#twocolumn
`Java`
#code{{
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.table.*;

public final class MainPanel {
  public JComponent makeUI() {
    String[] columnNames = {"user", "rwx"};
    Object[][] data = {
      {"owner", 7}, {"group", 6}, {"other", 5}
    };
    TableModel model = new DefaultTableModel(data, columnNames) {
      @Override public Class<?> getColumnClass(int column) {
        return getValueAt(0, column).getClass();
      }
    };
    JTable table = new JTable(model) {
      @Override public void updateUI() {
        super.updateUI();
        getColumnModel().getColumn(1).setCellRenderer(new CheckBoxesRenderer());
        getColumnModel().getColumn(1).setCellEditor(new CheckBoxesEditor());
      }
    };
    table.putClientProperty("terminateEditOnFocusLost", Boolean.TRUE);

    JPanel p = new JPanel(new BorderLayout());
    p.add(new JScrollPane(table));
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame frame = new JFrame();
      frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      frame.getContentPane().add(new MainPanel().makeUI());
      frame.setSize(320, 240);
      frame.setLocationRelativeTo(null);
      frame.setVisible(true);
    });
  }
}

class CheckBoxesPanel extends JPanel {
  protected static final String[] TITLES = {"r", "w", "x"};
  public JCheckBox[] buttons;
  @Override public void updateUI() {
    super.updateUI();
    setOpaque(false);
    setBackground(new Color(0x0, true));
    setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
    initButtons();
  }
  private void initButtons() {
    buttons = new JCheckBox[TITLES.length];
    for (int i = 0; i < buttons.length; i++) {
      JCheckBox b = new JCheckBox(TITLES[i]);
      b.setOpaque(false);
      b.setFocusable(false);
      b.setRolloverEnabled(false);
      b.setBackground(new Color(0x0, true));
      buttons[i] = b;
      add(b);
      add(Box.createHorizontalStrut(5));
    }
  }
  protected void updateButtons(Object v) {
    removeAll();
    initButtons();
    Integer i = v instanceof Integer ? (Integer) v : 0;
    buttons[0].setSelected((i & (1 << 2)) != 0);
    buttons[1].setSelected((i & (1 << 1)) != 0);
    buttons[2].setSelected((i & (1 << 0)) != 0);
  }
}

class CheckBoxesRenderer extends CheckBoxesPanel implements TableCellRenderer {
  @Override public void updateUI() {
    super.updateUI();
    setName("Table.cellRenderer");
  }
  @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    updateButtons(value);
    return this;
  }
}

class CheckBoxesEditor extends AbstractCellEditor implements TableCellEditor {
  private final CheckBoxesPanel panel = new CheckBoxesPanel() {
    @Override public void updateUI() {
      super.updateUI();
      EventQueue.invokeLater(() -> {
        ActionMap am = getActionMap();
        for (int i = 0; i < buttons.length; i++) {
          String title = TITLES[i];
          am.put(title, new AbstractAction(title) {
            @Override public void actionPerformed(ActionEvent e) {
              Arrays.stream(buttons)
              .filter(b -> b.getText().equals(title))
              .findFirst()
              .ifPresent(JCheckBox::doClick);
              fireEditingStopped();
            }
          });
        }
        InputMap im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0), TITLES[0]);
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), TITLES[1]);
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, 0), TITLES[2]);
      });
    }
  };
  @Override public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
    panel.updateButtons(value);
    return panel;
  }
  @Override public Object getCellEditorValue() {
    int i = 0;
    i = panel.buttons[0].isSelected() ? 1 << 2 | i : i;
    i = panel.buttons[1].isSelected() ? 1 << 1 | i : i;
    i = panel.buttons[2].isSelected() ? 1 << 0 | i : i;
    return i;
  }
}
}}
#twocolumn
`Kotlin`
#code(lang-kotlin){{
import java.awt.*
import java.awt.event.*
import java.util.*
import javax.swing.*
import javax.swing.event.*
import javax.swing.plaf.*
import javax.swing.table.*

fun makeUI(): JComponent {
  val columnNames = arrayOf("user", "rwx")
  val data = arrayOf(arrayOf<Any>("owner", 7), arrayOf<Any>("group", 6), arrayOf<Any>("other", 5))
  val model = object : DefaultTableModel(data, columnNames) {
    override fun getColumnClass(column: Int): Class<*> {
      return getValueAt(0, column).javaClass
    }
  }
  val table = object : JTable(model) {
    override fun updateUI() {
      super.updateUI()
      getColumnModel().getColumn(1).setCellRenderer(CheckBoxesRenderer())
      getColumnModel().getColumn(1).setCellEditor(CheckBoxesEditor())
    }
  }
  table.putClientProperty("terminateEditOnFocusLost", java.lang.Boolean.TRUE)
  return JPanel(BorderLayout(5, 5)).apply {
    add(JScrollPane(table))
  }
}

fun main(args: Array<String>) {
  EventQueue.invokeLater {
    JFrame("kotlin swing").apply {
      defaultCloseOperation = JFrame.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }
  }
}

open class CheckBoxesPanel() : JPanel() {
  public val buttons = arrayOf(JCheckBox(Permission.READ.toString()), JCheckBox(Permission.WRITE.toString()), JCheckBox(Permission.EXECUTE.toString()))

  override fun updateUI() {
    super.updateUI()
    setOpaque(false)
    setBackground(Color(0x0, true))
    setLayout(BoxLayout(this, BoxLayout.X_AXIS))
    EventQueue.invokeLater({ initButtons() })
  }

  private fun initButtons() {
    for (b in buttons) {
      b.setOpaque(false)
      b.setFocusable(false)
      b.setRolloverEnabled(false)
      b.setBackground(Color(0x0, true))
      add(b)
      add(Box.createHorizontalStrut(5))
    }
  }

  public fun updateButtons(v: Any) {
    removeAll()
    initButtons()
    val i = v as ? Int ? : 0
    buttons[0].setSelected(i and (1 shl 2) != 0)
    buttons[1].setSelected(i and (1 shl 1) != 0)
    buttons[2].setSelected(i and (1 shl 0) != 0)
  }
}

enum class Permission(var str: String) {
  READ("r"), WRITE("w"), EXECUTE("x");
  override fun toString() = str
}

internal class CheckBoxesRenderer : CheckBoxesPanel(), TableCellRenderer {
  override fun updateUI() {
    super.updateUI()
    setName("Table.cellRenderer")
  }
  override fun getTableCellRendererComponent(table: JTable, value: Any, isSelected: Boolean, hasFocus: Boolean, row: Int, column: Int): Component {
    updateButtons(value)
    return this
  }
}

internal class CheckBoxesEditor : AbstractCellEditor(), TableCellEditor {
  private val panel = object : CheckBoxesPanel() {
    override fun updateUI() {
      super.updateUI()
      EventQueue.invokeLater({
        val am = getActionMap()
        for (i in buttons.indices) {
          val title = buttons[i].getText()
          am.put(title, object : AbstractAction(title) {
            override fun actionPerformed(e: ActionEvent) {
              Arrays.stream(buttons)
                .filter({ b -> b.getText() == title })
                .findFirst()
                .ifPresent({ it.doClick() })
              fireEditingStopped()
            }
          })
        }
        val im = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW)
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_R, 0), Permission.READ.toString())
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_W, 0), Permission.WRITE.toString())
        im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, 0), Permission.EXECUTE.toString())
      })
    }
  }

  override fun getTableCellEditorComponent(table: JTable, value: Any, isSelected: Boolean, row: Int, column: Int): Component {
    panel.updateButtons(value)
    return panel
  }

  override fun getCellEditorValue(): Any {
    var i = 0
    i = if (panel.buttons[0].isSelected()) 1 shl 2 or i else i
    i = if (panel.buttons[1].isSelected()) 1 shl 1 or i else i
    i = if (panel.buttons[2].isSelected()) 1 shl 0 or i else i
    return i
  }
}
}}
#twocolumn

** Arrays.asList(...) [#vbc11c20]
- `IntelliJ`の自動変換では`Arrays.asList(...)`の型パラメータを自動的に変換してくれない
-- `Arrays.<Component>asList(...)`と型パラメータを明示しても省略される
-- `for (c in Arrays.asList(...)) {`に変換されて`error: none of the following functions can be called with the arguments supplied:`になる
-- `for (c: Component in Arrays.asList<Component>(...)) {`、または`for (c in listOf<Component>(...)) {`などに手動で修正(`for (c: Component in Arrays<Component>.asList(...)) {`も可能?)
- 型引数
-- `Java`と`Kotlin`では型引数を付ける位置が異なるので注意

-- `Java`: `Arrays.<Component>asList(...)`
> [https://docs.oracle.com/javase/tutorial/java/generics/methods.html Generic Methods (The Java™ Tutorials > Learning the Java Language > Generics (Updated))]
> For static generic methods, the type parameter section must appear ''before'' the method's return type.

-- `Kotlin`: `Arrays.asList<Component>(...)`
> [https://kotlinlang.org/docs/reference/generics.html Generics: in, out, where - Kotlin Programming Language]
> To call a generic function, specify the type arguments at the call site ''after'' the name of the function:

-- `Kotlin`で`Java`風に`for (c in Arrays.<Component>asList(...)) {`を使用すると、`hello.kt:97:26: error: expecting an element`とエラーになる

#twocolumn
`Java`
#code{{
Box box = Box.createVerticalBox();
for (Component c: Arrays.<Component>asList(textField1, textField2, combo3, combo4)) {
  box.add(c);
  box.add(Box.createVerticalStrut(5));
}
// for (Component c: Arrays.asList(...)) { でも同じ変換結果になる
}}

#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
val box = Box.createVerticalBox()
for (c in Arrays.asList(textField1, textField2, combo3, combo4)) {
  box.add(c)
  box.add(Box.createVerticalStrut(5))
}
// hello.kt:100:17: error: none of the following functions can be called with the arguments supplied:
// public open fun add(p0: Component!): Component! defined in javax.swing.Box
// public open fun add(p0: PopupMenu!): Unit defined in javax.swing.Box
//             box.add(c)
//                 ^
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
val box = Box.createVerticalBox()
for (c in Arrays.asList<Component>(textField1, textField2, combo3, combo4)) {
  box.add(c);
  box.add(Box.createVerticalStrut(5));
}
// or:
// for (c in listOf<Component>(textField1, textField2, combo3, combo4)) {
// for (c in Arrays<Component>.asList(textField1, textField2, combo3, combo4)) {
}}
#twocolumn

** JTree, ActionListener [#SAM]
- `IntelliJ`の自動変換では`ActionListener`などの関数型インターフェースのラムダ式をうまく変換できない場合がある
-- %%`AbstractCellEditor#stopCellEditing()`は`boolean`を返すので%%`error: type mismatch: inferred type is (???) -> Boolean but ActionListener? was expected`とエラーになる
- [https://kotlinlang.org/docs/reference/java-interop.html#sam-conversions SAM Conversions - Calling Java from Kotlin - Kotlin Programming Language]

#twocolumn
`Java`
#code{{
protected ActionListener handler;
//...
handler = e -> stopCellEditing();
}}

#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
protected var handler: ActionListener? = null
//...
handler = { e -> stopCellEditing() }
// error: type mismatch: inferred type is (???) -> Boolean but ActionListener? was expected
//             handler = { e -> stopCellEditing() }
// error: cannot infer a type for this parameter. Please specify it explicitly.
//             handler = { e -> stopCellEditing() }
// error: modifier 'override' is not applicable to 'local function'
//               override fun actionPerformed(e: ActionEvent) : Unit {
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
protected var handler: ActionListener? = null
//...
handler = ActionListener { stopCellEditing() }
// or:
handler = object: ActionListener {
  override fun actionPerformed(e: ActionEvent) : Unit { // Unitは無くても可
    stopCellEditing()
  }
}
}}
#twocolumn

- `IntelliJ`の自動変換で、以下のようなエラーになる場合は、`Object`を`Any`に手動変換する
 error: class 'CheckBoxNodeRenderer' is not abstract and does not implement abstract member public abstract fun getTreeCellRendererComponent(p0: JTree!, p1: Any!, p2: Boolean, p3: Boolean, p4: Boolean, p5: Int, p6: Boolean): Component! defined in javax.swing.tree.TreeCellRenderer
 internal class CheckBoxNodeRenderer : TreeCellRenderer {

- getterのみオーバーライド

#twocolumn
`Java`
#code{{
class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
  @Override public Object getCellEditorValue() {
    return new CheckBoxNode(checkBox.getText(), checkBox.isSelected());
  }
  //...
}}

#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
val cellEditorValue: Any
  override get() = CheckBoxNode(checkBox.getText(), checkBox.isSelected())
//error: modifier 'override' is not applicable to 'getter'
//       override get() = CheckBoxNode(checkBox.getText(), checkBox.isSelected())
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
  override fun getCellEditorValue(): Any {
    return CheckBoxNode(checkBox.getText(), checkBox.isSelected())
  }
}}

#twocolumn

#twocolumn
`Java`
#code{{
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;

public class LeafCheckBoxTreeTest {
  private JComponent makeUI() {
    JTree tree = new JTree();
    tree.setEditable(true);
    tree.setCellRenderer(new CheckBoxNodeRenderer());
    tree.setCellEditor(new CheckBoxNodeEditor());
    tree.setRowHeight(18);

    boolean b = true;
    TreeModel model = tree.getModel();
    DefaultMutableTreeNode root = (DefaultMutableTreeNode) model.getRoot();
    Enumeration<?> e = root.breadthFirstEnumeration();
    while (e.hasMoreElements()) {
      DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
      String s = Objects.toString(node.getUserObject(), "");
      node.setUserObject(new CheckBoxNode(s, b));
      b ^= true;
    }
    return new JScrollPane(tree);
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      UIManager.put("swing.boldMetal", Boolean.FALSE);
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new LeafCheckBoxTreeTest().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class CheckBoxNodeRenderer implements TreeCellRenderer {
  private final JCheckBox checkBox = new JCheckBox();
  private final DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
  @Override public Component getTreeCellRendererComponent(
      JTree tree, Object value, boolean selected, boolean expanded,
      boolean leaf, int row, boolean hasFocus) {
    if (leaf && value instanceof DefaultMutableTreeNode) {
      checkBox.setEnabled(tree.isEnabled());
      checkBox.setFont(tree.getFont());
      checkBox.setOpaque(false);
      checkBox.setFocusable(false);
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if (userObject instanceof CheckBoxNode) {
        CheckBoxNode node = (CheckBoxNode) userObject;
        checkBox.setText(node.text);
        checkBox.setSelected(node.selected);
      }
      return checkBox;
    }
    return renderer.getTreeCellRendererComponent(
        tree, value, selected, expanded, leaf, row, hasFocus);
  }
}

class CheckBoxNodeEditor extends AbstractCellEditor implements TreeCellEditor {
  private final JCheckBox checkBox = new JCheckBox() {
    protected transient ActionListener handler;
    @Override public void updateUI() {
      removeActionListener(handler);
      super.updateUI();
      setOpaque(false);
      setFocusable(false);
      handler = e -> stopCellEditing();
      addActionListener(handler);
    }
  };
  @Override public Component getTreeCellEditorComponent(
      JTree tree, Object value, boolean selected, boolean expanded,
      boolean leaf, int row) {
    if (leaf && value instanceof DefaultMutableTreeNode) {
      Object userObject = ((DefaultMutableTreeNode) value).getUserObject();
      if (userObject instanceof CheckBoxNode) {
        checkBox.setSelected(((CheckBoxNode) userObject).selected);
      } else {
        checkBox.setSelected(false);
      }
      checkBox.setText(value.toString());
    }
    return checkBox;
  }
  @Override public Object getCellEditorValue() {
    return new CheckBoxNode(checkBox.getText(), checkBox.isSelected());
  }
  @Override public boolean isCellEditable(EventObject e) {
    if (e instanceof MouseEvent && e.getSource() instanceof JTree) {
      MouseEvent me = (MouseEvent) e;
      JTree tree = (JTree) me.getComponent();
      TreePath path = tree.getPathForLocation(me.getX(), me.getY());
      Object o = path.getLastPathComponent();
      if (o instanceof TreeNode) {
        return ((TreeNode) o).isLeaf();
      }
    }
    return false;
  }
}

class CheckBoxNode {
  public final String text;
  public final boolean selected;
  protected CheckBoxNode(String text, boolean selected) {
    this.text = text;
    this.selected = selected;
  }
  @Override public String toString() {
    return text;
  }
}
}}

#twocolumn
`Kotlin`
#code(lang-kotlin){{
import java.awt.*
import java.awt.event.*
import java.util.*
import javax.swing.*
import javax.swing.event.*
import javax.swing.tree.*

private fun makeUI(): JComponent {
  val tree = JTree()
  tree.setEditable(true)
  tree.setCellRenderer(CheckBoxNodeRenderer())
  tree.setCellEditor(CheckBoxNodeEditor())
  tree.setRowHeight(18)

  var b = true
  val model = tree.getModel()
  val root = model.getRoot() as DefaultMutableTreeNode
  val e = root.breadthFirstEnumeration()
  while (e.hasMoreElements()) {
    val node = e.nextElement() as DefaultMutableTreeNode
    val s = Objects.toString(node.getUserObject(), "")
    node.setUserObject(CheckBoxNode(s, b))
    b = b xor true
  }
  return JScrollPane(tree)
}

fun main(args: Array<String>) {
  EventQueue.invokeLater {
    UIManager.put("swing.boldMetal", false)
    JFrame("kotlin swing").apply {
      defaultCloseOperation = JFrame.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }
  }
}

internal class CheckBoxNodeRenderer : TreeCellRenderer {
  private val checkBox = JCheckBox()
  private val renderer = DefaultTreeCellRenderer()
  override fun getTreeCellRendererComponent(
    tree: JTree, value: Any, selected: Boolean, expanded: Boolean,
    leaf: Boolean, row: Int, hasFocus: Boolean): Component {
    if (leaf && value is DefaultMutableTreeNode) {
      checkBox.setEnabled(tree.isEnabled())
      checkBox.setFont(tree.getFont())
      checkBox.setOpaque(false)
      checkBox.setFocusable(false)
      val userObject = value.getUserObject()
      if (userObject is CheckBoxNode) {
        val node = userObject // as CheckBoxNode
        checkBox.setText(node.text)
        checkBox.setSelected(node.selected)
      }
      return checkBox
    }
    return renderer.getTreeCellRendererComponent(
      tree, value, selected, expanded, leaf, row, hasFocus)
  }
}

internal class CheckBoxNodeEditor : AbstractCellEditor(), TreeCellEditor {
  private val checkBox = object : JCheckBox() {
    protected var handler: ActionListener? = null
    override fun updateUI() {
      removeActionListener(handler)
      super.updateUI()
      setOpaque(false)
      setFocusable(false)
      // handler = object: ActionListener {
      //   override fun actionPerformed(e: ActionEvent) : Unit {
      //     stopCellEditing()
      //   }
      // }
      handler = ActionListener { stopCellEditing() }
      addActionListener(handler)
    }
  }

  override fun getTreeCellEditorComponent(
      tree: JTree, value: Any, selected: Boolean, expanded: Boolean,
      leaf: Boolean, row: Int): Component {
    if (leaf && value is DefaultMutableTreeNode) {
      val userObject = value.getUserObject()
      if (userObject is CheckBoxNode) {
        checkBox.setSelected(userObject.selected)
      } else {
        checkBox.setSelected(false)
      }
      checkBox.setText(value.toString())
    }
    return checkBox
  }

  override fun getCellEditorValue(): Any {
    return CheckBoxNode(checkBox.getText(), checkBox.isSelected())
  }

  override fun isCellEditable(e: EventObject): Boolean {
    if (e is MouseEvent && e.getSource() is JTree) {
      val tree = e.getComponent() as JTree
      val path = tree.getPathForLocation(e.getX(), e.getY())
      val o = path.getLastPathComponent()
      if (o is TreeNode) {
        return o.isLeaf()
      }
    }
    return false
  }
}

internal class CheckBoxNode public constructor(val text: String, val selected: Boolean) {
  override fun toString(): String {
    return text
  }
}
}}
#twocolumn

** メソッド参照 [#MethodReference]
- `IntelliJ`の自動変換ではメソッド参照の変換が苦手?

#twocolumn
`Java`
#code{{
public static Stream<TreeNode> children(TreeNode node) {
  Class<TreeNode> clz = TreeNode.class;
  return Collections.list((Enumeration<?>) node.children())
    .stream().filter(clz::isInstance).map(clz::cast);
}
}}
#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
fun children(node: TreeNode): Stream<TreeNode> {
  val clz = TreeNode::class.java
  return Collections.list(node.children() as Enumeration<*>)
    .stream().filter(???({ clz!!.isInstance() })).map(clz!!.cast)
}
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
fun children(node: TreeNode): Stream<TreeNode> {
  val clz = TreeNode::class.java
  return Collections.list(node.children() as Enumeration<*>)
    .stream().filter(clz::isInstance).map(clz::cast)
    // .filterIsInstance(clz).stream()
}
}}

`Kotlin`(手動修正): `filterIsInstance`が便利
#code(lang-kotlin){{
fun children(node: TreeNode): List<TreeNode> {
  return (node.children() as Enumeration<*>)
    .toList().filterIsInstance(TreeNode::class.java)
}
}}
#twocolumn

- [https://docs.oracle.com/javase/jp/8/docs/api/java/awt/Container.html#add-java.awt.Component- Container#add(Component)]メソッドは戻り値として`Component`を返す
-- `Java`の場合、終端処理の`forEach(p::add)`では戻り値を無視して`Container#add(Component)`をメソッド参照することが可能
-- `Kotlin`の場合、`forEach(p::add)`とすると、`add`メソッドで引数が`Component!`、戻り値が`Unit`になるメソッドを探すためメソッド参照で以下のようなエラーになる
--- [https://discuss.kotlinlang.org/t/function-reference-ignoring-the-returned-value/2442 Function reference: ignoring the returned value - Support - Kotlin Discussions]
--- `map(p::add)`にすればエラーは回避できる

 hello.kt:38:66: error: none of the following functions can be called with the arguments supplied:
 public open fun add(p0: Component!): Component! defined in javax.swing.JPanel
 public open fun add(p0: Component!, p1: Any!): Unit defined in javax.swing.JPanel
 public open fun add(p0: Component!, p1: Any!, p2: Int): Unit defined in javax.swing.JPanel
 public open fun add(p0: Component!, p1: Int): Component! defined in javax.swing.JPanel
 public open fun add(p0: PopupMenu!): Unit defined in javax.swing.JPanel
 public open fun add(p0: String!, p1: Component!): Component! defined in javax.swing.JPanel
     listOf(button1, button2, button3, button4).forEach(p::add)
                                                           ^

#twocolumn
`Java`
#code{{
JPanel p = new JPanel(new GridLayout(0, 1, 2, 2));
Arrays.asList(button1, button2).forEach(p::add);
}}

#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
val p = JPanel(GridLayout(0, 1, 2, 2))
Arrays.asList(button1, button2).forEach( ?? ? ({ p.add() }))
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
val p = JPanel(GridLayout(0, 1, 2, 2))
listOf(button1, button2).forEach({ b -> p.add(b) })
//or listOf(button1, button2).map(p::add)
}}

#twocolumn

** BigDecimal [#zb932a10]
- `IntelliJ`の自動変換では`error: unresolved reference: intValue`とエラーになるので、手動で`toInt()`に修正する必要がある

#twocolumn
`Java`
#code{{
double d = delta.y * GRAVITY;
int ia = (int) d;
int ib = (int) Math.floor(d);
int ic = new BigDecimal(d).setScale(0, RoundingMode.DOWN).intValue();
System.out.format("%d %d %d%n", ia, ib, ic);
}}

#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
val d = delta.y * GRAVITY
val ia = d.toInt()
val ib = Math.floor(d) as Int
val ic = BigDecimal(d).setScale(0, RoundingMode.DOWN).intValue()
System.out.format("%d %d %d%n", ia, ib, ic)
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
val d = delta.y * GRAVITY
val ia = d.toInt()
val ib = Math.floor(d).toInt()
val ic = BigDecimal(d).setScale(0, RoundingMode.DOWN).toInt()
println("${ia} ${ib} ${ic}")
}}

#twocolumn

** Comparator [#Comparator]
- `IntelliJ`の自動変換では`error: calls to static methods in Java interfaces are prohibited in JVM target 1.6. Recompile with '-jvm-target 1.8'`とエラーになる
-- `kotlinc hello.kt -jvm-target 1.8 -include-runtime -d hello.jar`と`kotlinc`にオプションを追加
- `IntelliJ`の自動変換では`error: using 'sort(kotlin.Comparator<in T> /* = java.util.Comparator<in T> */): Unit' is an error. Use sortWith(comparator) instead.`とエラーになる
-- `list.sort(...)`を`list.sortWith(...)`に修正
- `IntelliJ`の自動変換では`error: type inference failed: Not enough information to infer parameter T in fun <T : Any!> comparingInt(p0: ((T!) -> Int)!): Comparator<T!>!`とエラーになる
-- `Comparator.comparingInt({ l -> l.get(0) })`を`Comparator.comparingInt<List<Int>>({ l -> l.get(0) })`に修正

#twocolumn
`Java`
#code{{
import java.util.*;
public class SortTest {
  public static void main(String[] args) {
    List<List<Integer>> list = Arrays.asList(
      Arrays.asList(15, 20, 35),
      Arrays.asList(30, 45, 72),
      Arrays.asList(15, 20, 31),
      Arrays.asList(27, 33, 59),
      Arrays.asList(27, 35, 77));
    list.sort(Comparator.<List<Integer>>comparingInt(l -> l.get(0))
      .thenComparingInt(l -> l.get(1))
      .thenComparingInt(l -> l.get(2))
      .reversed());
    System.out.println(list);
  }
}
}}

#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
fun main(args: Array<String>) {
  val list = Arrays.asList(
    Arrays.asList(15, 20, 35),
    Arrays.asList(30, 45, 72),
    Arrays.asList(15, 20, 31),
    Arrays.asList(27, 33, 59),
    Arrays.asList(27, 35, 77))
  list.sort(Comparator.comparingInt({ l -> l.get(0) })
    .thenComparingInt({ l -> l.get(1) })
    .thenComparingInt({ l -> l.get(2) })
    .reversed())
  System.out.println(list)
}
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
fun main(args: Array<String>) {
  val list = Arrays.asList(
    Arrays.asList(15, 20, 35),
    Arrays.asList(30, 45, 72),
    Arrays.asList(15, 20, 31),
    Arrays.asList(27, 33, 59),
    Arrays.asList(27, 35, 77))
  list.sortWith(Comparator.comparingInt<List<Int>>({ l -> l.get(0) })
    .thenComparingInt({ l -> l.get(1) })
    .thenComparingInt({ l -> l.get(2) })
    .reversed())
  System.out.println(list)
}
}}

`Kotlin`(手動修正)
#code(lang-kotlin){{
fun main(args: Array<String>) {
  val list = listOf<List<Int>>(
    listOf(15, 20, 35),
    listOf(30, 45, 72),
    listOf(15, 20, 31),
    listOf(27, 33, 59),
    listOf(27, 35, 77))
  val l = list.sortedWith(compareBy({ it.get(0) }, { it.get(1) }, { it.get(2) })).reversed()
  println(l)
}
}}

#twocolumn

** 境界型パラメータ [#BoundedTypeParameter]
- `IntelliJ`の自動変換で以下のような境界型パラメータをもつ`Java`クラスを変換し、`kotlinc`でコンパイルすると無駄な境界型パラメータが存在すると警告してくれる
-- `warning: 'LocalDate' is a final type, and thus a value of the type parameter is predetermined`
-- `LocalDate`クラスは`final`なので継承不可のため、境界型パラメータは無意味
-- `ECJ`(`Eclipse Compiler for Java`)も`The type parameter T should not be bounded by the final type LocalDate. Final types cannot be further extended`と警告してくれる
-- `Java`側を`class CalendarViewTableModel<T extends TemporalAdjuster> extends DefaultTableModel {`か、`class CalendarViewTableModel extends DefaultTableModel {`に修正する

#twocolumn
`Java`
#code{{
class CalendarViewTableModel<T extends LocalDate> extends DefaultTableModel {
  public CalendarViewTableModel(T date) {
}}

#twocolumn
`Kotlin`(自動変換)
#code(lang-kotlin){{
class CalendarViewTableModel<T : LocalDate> public constructor(date: T) : DefaultTableModel() {
}}
#twocolumn

** SwingWorker [#SwingWorker]
- [https://www.pushing-pixels.org/2018/08/07/replacing-swingworker-with-kotlin-coroutines.html Replacing SwingWorker with Kotlin coroutines ・ Pushing Pixels]を参考に、`SwingWorker`を`kotlin-coroutines`に置き換えるテストをしているが、`unresolved reference`になる
-- `lib`以下に`kotlinx-coroutines-jdk8-0.24.0.jar`、`kotlinx-coroutines-swing-0.24.0.jar`などを配置
- [https://www.pushing-pixels.org/2018/08/07/replacing-swingworker-with-kotlin-coroutines.html Replacing SwingWorker with Kotlin coroutines ・ Pushing Pixels]を参考に、%%`SwingWorker`を`kotlin-coroutines`に置き換えるテストをしているが、`unresolved reference`になる%% `kotlin 1.3.0`、`kotlinx.coroutines 1.0.0`で動作確認
-- [https://github.com/Kotlin/kotlinx.coroutines/blob/master/COMPATIBILITY.md#migration-to-100-version-with-kotlin-13 kotlinx.coroutines/COMPATIBILITY.md at master · Kotlin/kotlinx.coroutines]
-- 次は[http://d.hatena.ne.jp/nowokay/20180809#1533777641 Project LoomでJavaでの継続(Continuation)を試す - きしだのはてな]を試す予定

    $ kotlinc -cp lib hello.kt -Xcoroutines=enable -include-runtime -d hello.jar
    hello.kt:26:5: error: unresolved reference: launch
        launch(Swing) {
        ^
    ...
#code{{
group 'Example'
version '1.0-SNAPSHOT'

- [http://d.hatena.ne.jp/nowokay/20180809#1533777641 Project LoomでJavaでの継続(Continuation)を試す - きしだのはてな]
apply plugin: 'java'
apply plugin: 'kotlin'
apply plugin: 'application'

mainClassName = 'ExampleKt'

buildscript {
  ext.kotlin_version = '1.3.0'
  ext.coroutine_version = '1.0.0' // '0.30.2-eap13'
  repositories {
    mavenCentral()
  }
  dependencies {
    classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  }
}

sourceCompatibility = 1.8
compileKotlin {
  kotlinOptions.jvmTarget = "1.8"
}
compileTestKotlin {
  kotlinOptions.jvmTarget = "1.8"
}

kotlin {
  repositories {
    mavenCentral()
  }
  dependencies {
    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutine_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-swing:$coroutine_version"
    implementation "org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$coroutine_version"
  }
}

repositories {
  mavenCentral()
}

dependencies {
  compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
}
}}

#twocolumn
`Java`
#code{{
import java.awt.*;
import java.awt.geom.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Random;
import javax.swing.*;
import javax.swing.plaf.basic.BasicProgressBarUI;

public class MainPanel extends JPanel {
  protected final JProgressBar progress1 = new JProgressBar() {
    @Override public void updateUI() {
      super.updateUI();
      setUI(new ProgressCircleUI());
      setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25));
    }
  };
  protected final JProgressBar progress2 = new JProgressBar() {
    @Override public void updateUI() {
      super.updateUI();
      setUI(new ProgressCircleUI());
      setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25));
    }
  };
  public MainPanel() {
    super(new BorderLayout());
    progress1.setForeground(new Color(0xAAFFAAAA));
    progress2.setStringPainted(true);
    progress2.setFont(progress2.getFont().deriveFont(24f));

    JSlider slider = new JSlider();
    slider.putClientProperty("Slider.paintThumbArrowShape", Boolean.TRUE);
    progress1.setModel(slider.getModel());

    JButton button = new JButton("start");
    button.addActionListener(e -> {
      JButton b = (JButton) e.getSource();
      b.setEnabled(false);
      SwingWorker<String, Void> worker = new BackgroundTask() {
        @Override public void done() {
          if (b.isDisplayable()) {
            b.setEnabled(true);
          }
        }
      };
      worker.addPropertyChangeListener(new ProgressListener(progress2));
      worker.execute();
    });

    JPanel p = new JPanel(new GridLayout(1, 2));
    p.add(progress1);
    p.add(progress2);

    add(slider, BorderLayout.NORTH);
    add(p);
    add(button, BorderLayout.SOUTH);
    setPreferredSize(new Dimension(320, 240));
  }
  public static void main(String... args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGui();
      }
    });
  }
  public static void createAndShowGui() {
    try {
      UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
    } catch (ClassNotFoundException | InstantiationException
               | IllegalAccessException | UnsupportedLookAndFeelException ex) {
      ex.printStackTrace();
    }
    JFrame frame = new JFrame("@title@");
    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    frame.getContentPane().add(new MainPanel());
    frame.pack();
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
  }
}

class ProgressCircleUI extends BasicProgressBarUI {
  @Override public Dimension getPreferredSize(JComponent c) {
    Dimension d = super.getPreferredSize(c);
    int v = Math.max(d.width, d.height);
    d.setSize(v, v);
    return d;
  }
  @Override public void paint(Graphics g, JComponent c) {
    // public void paintDeterminate(Graphics g, JComponent c) {
    Insets b = progressBar.getInsets(); // area for border
    int barRectWidth = progressBar.getWidth() - b.right - b.left;
    int barRectHeight = progressBar.getHeight() - b.top - b.bottom;
    if (barRectWidth <= 0 || barRectHeight <= 0) {
      return;
    }

    Graphics2D g2 = (Graphics2D) g.create();
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);

    double degree = 360 * progressBar.getPercentComplete();
    double sz = Math.min(barRectWidth, barRectHeight);
    double cx = b.left + barRectWidth * .5;
    double cy = b.top + barRectHeight * .5;
    double or = sz * .5;
    // double ir = or - 20;
    double ir = or * .5; // .8;
    Shape inner = new Ellipse2D.Double(cx - ir, cy - ir, ir * 2, ir * 2);
    Shape outer = new Ellipse2D.Double(cx - or, cy - or, sz, sz);
    Shape sector = new Arc2D.Double(cx - or, cy - or, sz, sz, 90 - degree, degree, Arc2D.PIE);

    Area foreground = new Area(sector);
    Area background = new Area(outer);
    Area hole = new Area(inner);

    foreground.subtract(hole);
    background.subtract(hole);

    // draw the track
    g2.setPaint(new Color(0xDDDDDD));
    g2.fill(background);

    // draw the circular sector
    // AffineTransform at = AffineTransform.getScaleInstance(-1.0, 1.0);
    // at.translate(-(barRectWidth + b.left * 2), 0);
    // AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(degree), cx, cy);
    // g2.fill(at.createTransformedShape(area));
    g2.setPaint(progressBar.getForeground());
    g2.fill(foreground);
    g2.dispose();

    // Deal with possible text painting
    if (progressBar.isStringPainted()) {
      paintString(g, b.left, b.top, barRectWidth, barRectHeight, 0, b);
    }
  }
}

class BackgroundTask extends SwingWorker<String, Void> {
  private final Random rnd = new Random();
  @Override public String doInBackground() {
    int current = 0;
    int lengthOfTask = 100;
    while (current <= lengthOfTask && !isCancelled()) {
      try { // dummy task
        Thread.sleep(rnd.nextInt(50) + 1);
      } catch (InterruptedException ex) {
        return "Interrupted";
      }
      setProgress(100 * current / lengthOfTask);
      current++;
    }
    return "Done";
  }
}

class ProgressListener implements PropertyChangeListener {
  private final JProgressBar progressBar;
  protected ProgressListener(JProgressBar progressBar) {
    this.progressBar = progressBar;
    this.progressBar.setValue(0);
  }
  @Override public void propertyChange(PropertyChangeEvent e) {
    String strPropertyName = e.getPropertyName();
    if ("progress".equals(strPropertyName)) {
      progressBar.setIndeterminate(false);
      int progress = (Integer) e.getNewValue();
      progressBar.setValue(progress);
    }
  }
}
}}

#twocolumn
`Kotlin`(自動変換)
`Kotlin`(`SwingWorker`版)
#code(lang-kotlin){{
import java.awt.*
import java.awt.geom.*
import java.beans.PropertyChangeEvent
import java.beans.PropertyChangeListener
import java.util.Random
import javax.swing.*
import javax.swing.plaf.basic.BasicProgressBarUI

class MainPanel : JPanel(BorderLayout()) {
    protected val progress1: JProgressBar = object : JProgressBar() {
        @Override
        fun updateUI() {
            super.updateUI()
            setUI(ProgressCircleUI())
            setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25))
        }
private fun makeUI(): Component {
  val progress = object : JProgressBar() {
    override fun updateUI() {
      super.updateUI()
      setUI(ProgressCircleUI())
      setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25))
    }
    protected val progress2: JProgressBar = object : JProgressBar() {
        @Override
        fun updateUI() {
            super.updateUI()
            setUI(ProgressCircleUI())
            setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25))
  }
  val button = JButton("start")
  button.addActionListener { e ->
    val b = e.getSource() as JButton
    b.setEnabled(false);
    val worker = object : BackgroundTask() {
      override fun done() {
        if (b.isDisplayable()) {
          b.setEnabled(true)
        }
      }
    }
    worker.addPropertyChangeListener(ProgressListener(progress))
    worker.execute()
  }

    init {
        progress1.setForeground(Color(-0x55005556))
        progress2.setStringPainted(true)
        progress2.setFont(progress2.getFont().deriveFont(24f))
  return JPanel(BorderLayout(5, 5)).apply {
    setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5))
    add(progress)
    add(button, BorderLayout.SOUTH)
  }
}

        val slider = JSlider()
        slider.putClientProperty("Slider.paintThumbArrowShape", Boolean.TRUE)
        progress1.setModel(slider.getModel())

        val button = JButton("start")
        button.addActionListener({ e ->
            val b = e.getSource() as JButton
            b.setEnabled(false)
            val worker = object : BackgroundTask() {
                @Override
                fun done() {
                    if (b.isDisplayable()) {
                        b.setEnabled(true)
                    }
                }
            }
            worker.addPropertyChangeListener(ProgressListener(progress2))
            worker.execute()
        })

        val p = JPanel(GridLayout(1, 2))
        p.add(progress1)
        p.add(progress2)

        add(slider, BorderLayout.NORTH)
        add(p)
        add(button, BorderLayout.SOUTH)
        setPreferredSize(Dimension(320, 240))
fun main(args: Array<String>) {
  EventQueue.invokeLater {
    JFrame("kotlin swing").apply {
      defaultCloseOperation = JFrame.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }

    companion object {
        fun main(vararg args: String) {
            EventQueue.invokeLater(object : Runnable() {
                @Override
                fun run() {
                    createAndShowGui()
                }
            })
        }

        fun createAndShowGui() {
            try {
                UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
            } catch (ex: ClassNotFoundException) {
                ex.printStackTrace()
            } catch (ex: InstantiationException) {
                ex.printStackTrace()
            } catch (ex: IllegalAccessException) {
                ex.printStackTrace()
            } catch (ex: UnsupportedLookAndFeelException) {
                ex.printStackTrace()
            }

            val frame = JFrame("@title@")
            frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
            frame.getContentPane().add(MainPanel())
            frame.pack()
            frame.setLocationRelativeTo(null)
            frame.setVisible(true)
        }
    }
  }
}

internal class ProgressCircleUI : BasicProgressBarUI() {
    @Override
    fun getPreferredSize(c: JComponent): Dimension {
        val d = super.getPreferredSize(c)
        val v = Math.max(d.width, d.height)
        d.setSize(v, v)
        return d
  override fun getPreferredSize(c: JComponent): Dimension {
    val d = super.getPreferredSize(c)
    val v = Math.max(d.width, d.height)
    d.setSize(v, v)
    return d;
  }
  override fun paint(g: Graphics, c: JComponent) {
    val b = progressBar.getInsets(); // area for border
    val barRectWidth = progressBar.getWidth() - b.right - b.left
    val barRectHeight = progressBar.getHeight() - b.top - b.bottom
    if (barRectWidth <= 0 || barRectHeight <= 0) {
      return
    }

    @Override
    fun paint(g: Graphics, c: JComponent) {
        // public void paintDeterminate(Graphics g, JComponent c) {
        val b = progressBar.getInsets() // area for border
        val barRectWidth = progressBar.getWidth() - b.right - b.left
        val barRectHeight = progressBar.getHeight() - b.top - b.bottom
        if (barRectWidth <= 0 || barRectHeight <= 0) {
            return
        }
    val g2 = g.create() as Graphics2D
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)

        val g2 = g.create() as Graphics2D
        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)
    val degree = 360 * progressBar.getPercentComplete()
    val sz = 1.0 * Math.min(barRectWidth, barRectHeight)
    val cx = b.left + barRectWidth * .5
    val cy = b.top + barRectHeight * .5
    val or = sz * .5
    val ir = or * .5

        val degree = 360 * progressBar.getPercentComplete()
        val sz = Math.min(barRectWidth, barRectHeight)
        val cx = b.left + barRectWidth * .5
        val cy = b.top + barRectHeight * .5
        val or = sz * .5
        // double ir = or - 20;
        val ir = or * .5 // .8;
        val inner = Ellipse2D.Double(cx - ir, cy - ir, ir * 2, ir * 2)
        val outer = Ellipse2D.Double(cx - or, cy - or, sz, sz)
        val sector = Arc2D.Double(cx - or, cy - or, sz, sz, 90 - degree, degree, Arc2D.PIE)
    val inner = Ellipse2D.Double(cx - ir, cy - ir, ir * 2, ir * 2)
    val outer = Ellipse2D.Double(cx - or, cy - or, sz, sz)
    val sector = Arc2D.Double(cx - or, cy - or, sz, sz, 90 - degree, degree, Arc2D.PIE)

        val foreground = Area(sector)
        val background = Area(outer)
        val hole = Area(inner)
    val foreground = Area(sector)
    val background = Area(outer)
    val hole = Area(inner)

        foreground.subtract(hole)
        background.subtract(hole)
    foreground.subtract(hole)
    background.subtract(hole)

        // draw the track
        g2.setPaint(Color(0xDDDDDD))
        g2.fill(background)
    // draw the track
    g2.setPaint(Color(0xDDDDDD))
    g2.fill(background)

        // draw the circular sector
        // AffineTransform at = AffineTransform.getScaleInstance(-1.0, 1.0);
        // at.translate(-(barRectWidth + b.left * 2), 0);
        // AffineTransform at = AffineTransform.getRotateInstance(Math.toRadians(degree), cx, cy);
        // g2.fill(at.createTransformedShape(area));
        g2.setPaint(progressBar.getForeground())
        g2.fill(foreground)
        g2.dispose()
    // draw the circular sector
    g2.setPaint(progressBar.getForeground())
    g2.fill(foreground)
    g2.dispose()

        // Deal with possible text painting
        if (progressBar.isStringPainted()) {
            paintString(g, b.left, b.top, barRectWidth, barRectHeight, 0, b)
        }
    // Deal with possible text painting
    if (progressBar.isStringPainted()) {
        paintString(g, b.left, b.top, barRectWidth, barRectHeight, 0, b)
    }
  }
}

internal class BackgroundTask : SwingWorker<String, Void>() {
open class BackgroundTask : SwingWorker<String, Void>() {
    private val rnd = Random()
    @Override
    fun doInBackground(): String {
    override fun doInBackground(): String {
        var current = 0
        val lengthOfTask = 100
        while (current <= lengthOfTask && !isCancelled()) {
            try { // dummy task
                Thread.sleep(rnd.nextInt(50) + 1)
                Thread.sleep(1L + rnd.nextInt(50))
            } catch (ex: InterruptedException) {
                return "Interrupted"
            }

            setProgress(100 * current / lengthOfTask)
            current++
        }
        return "Done"
    }
}

internal class ProgressListener protected constructor(private val progressBar: JProgressBar) : PropertyChangeListener {
internal class ProgressListener constructor(private val progressBar: JProgressBar) : PropertyChangeListener {
    init {
        this.progressBar.setValue(0)
    }

    @Override
    fun propertyChange(e: PropertyChangeEvent) {
    override fun propertyChange(e: PropertyChangeEvent) {
        val strPropertyName = e.getPropertyName()
        if ("progress".equals(strPropertyName)) {
            progressBar.setIndeterminate(false)
            val progress = (e.getNewValue() as Integer).toInt()
            val progress = e.getNewValue() as Int
            progressBar.setValue(progress)
        }
    }
}
}}

`Kotlin`(手動修正)
`Kotlin`(`Coroutine`版)
#code(lang-kotlin){{
import java.awt.*
import java.awt.geom.*
import java.beans.PropertyChangeEvent
import java.beans.PropertyChangeListener
import java.util.Random
import java.awt.geom.Area
import java.awt.geom.Arc2D
import java.awt.geom.Ellipse2D
import javax.swing.*
import javax.swing.plaf.basic.BasicProgressBarUI
// import kotlinx.coroutines.*
// import kotlinx.coroutines.experimental.*

import kotlinx.coroutines.*
import kotlinx.coroutines.channels.*
import kotlinx.coroutines.swing.*

private fun makeUI(): Component {
  val progress = object : JProgressBar() {
    override fun updateUI() {
      super.updateUI()
      setUI(ProgressCircleUI())
      setBorder(BorderFactory.createEmptyBorder(25, 25, 25, 25))
    }
  }
  val button = JButton("start")
//   button.addActionListener { e ->
//     launch(Swing) {
//       val b = e.getSource() as JButton
//       b.setEnabled(false);
//       val channel = Channel()
//       async {
//         var current = 0;
//         val lengthOfTask = 100;
//         while (current <= lengthOfTask) { // && !isCancelled()) {
//           delay(1000L)
//           channel.send(100 * current / lengthOfTask);
//           current++;
//         }
//         // Close the channel as we're done processing
//         channel.close()
//       }
//       // The next loop keeps on going as long as the channel is not closed
//       for (y in channel) {
//         // println("Processing $y " + SwingUtilities.isEventDispatchThread())
//         // status.text = "Progress $y"
//         progress.setValue(y);
//       }
//       // status.text = "Done!"
//       // @Override public void done() {
//       if (b.isDisplayable()) {
//         b.setEnabled(true)
//       }
//     }
//   }
  button.addActionListener { e ->
    val b = e.getSource() as JButton
    b.setEnabled(false);
    val worker = object : BackgroundTask() {
      override fun done() {
        if (b.isDisplayable()) {
          b.setEnabled(true)
    GlobalScope.launch(Dispatchers.Swing) {
      val b = e.getSource() as JButton
      b.setEnabled(false);
      val channel = Channel<Int>()
      async {
        var current = 0;
        val lengthOfTask = 100;
        while (current <= lengthOfTask) {
          delay(20L)
          channel.send(100 * current / lengthOfTask);
          current++;
        }
        // Close the channel as we're done processing
        channel.close()
      }
      // The next loop keeps on going as long as the channel is not closed
      for (y in channel) {
        // println("Processing $y " + SwingUtilities.isEventDispatchThread())
        progress.setValue(y);
      }
      // status.text = "Done!"
      // @Override public void done() {
      if (b.isDisplayable()) {
        b.setEnabled(true)
      }
    }
    worker.addPropertyChangeListener(ProgressListener(progress))
    worker.execute()
  }

  return JPanel(BorderLayout(5, 5)).apply {
    setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5))
    add(progress)
    add(button, BorderLayout.SOUTH)
  }
}

fun main(args: Array<String>) {
  EventQueue.invokeLater {
    JFrame("kotlin swing").apply {
      defaultCloseOperation = JFrame.EXIT_ON_CLOSE
      add(makeUI())
      size = Dimension(320, 240)
      setLocationRelativeTo(null)
      setVisible(true)
    }
  }
}

internal class ProgressCircleUI : BasicProgressBarUI() {
  override fun getPreferredSize(c: JComponent): Dimension {
    val d = super.getPreferredSize(c)
    val v = Math.max(d.width, d.height)
    d.setSize(v, v)
    return d;
  }
  override fun paint(g: Graphics, c: JComponent) {
    val b = progressBar.getInsets(); // area for border
    val barRectWidth = progressBar.getWidth() - b.right - b.left
    val barRectHeight = progressBar.getHeight() - b.top - b.bottom
    if (barRectWidth <= 0 || barRectHeight <= 0) {
      return
    }

    val g2 = g.create() as Graphics2D
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON)

    val degree = 360 * progressBar.getPercentComplete()
    val sz = 1.0 * Math.min(barRectWidth, barRectHeight)
    val cx = b.left + barRectWidth * .5
    val cy = b.top + barRectHeight * .5
    val or = sz * .5
    val ir = or * .5

    val inner = Ellipse2D.Double(cx - ir, cy - ir, ir * 2, ir * 2)
    val outer = Ellipse2D.Double(cx - or, cy - or, sz, sz)
    val sector = Arc2D.Double(cx - or, cy - or, sz, sz, 90 - degree, degree, Arc2D.PIE)

    val foreground = Area(sector)
    val background = Area(outer)
    val hole = Area(inner)

    foreground.subtract(hole)
    background.subtract(hole)

    // draw the track
    g2.setPaint(Color(0xDDDDDD))
    g2.fill(background)

    // draw the circular sector
    g2.setPaint(progressBar.getForeground())
    g2.fill(foreground)
    g2.dispose()

    // Deal with possible text painting
    if (progressBar.isStringPainted()) {
        paintString(g, b.left, b.top, barRectWidth, barRectHeight, 0, b)
    }
  }
}

open class BackgroundTask : SwingWorker<String, Void>() {
    private val rnd = Random()
    override fun doInBackground(): String {
        var current = 0
        val lengthOfTask = 100
        while (current <= lengthOfTask && !isCancelled()) {
            try { // dummy task
                Thread.sleep(1L + rnd.nextInt(50))
            } catch (ex: InterruptedException) {
                return "Interrupted"
            }
            setProgress(100 * current / lengthOfTask)
            current++
        }
        return "Done"
    }
}

internal class ProgressListener constructor(private val progressBar: JProgressBar) : PropertyChangeListener {
    init {
        this.progressBar.setValue(0)
    }
    override fun propertyChange(e: PropertyChangeEvent) {
        val strPropertyName = e.getPropertyName()
        if ("progress".equals(strPropertyName)) {
            progressBar.setIndeterminate(false)
            val progress = e.getNewValue() as Int
            progressBar.setValue(progress)
        }
    }
}
}}

#twocolumn

* コメント [#comment]
#comment
#comment