20220225のJavaに関する記事は7件です。

【Java】コレクションを扱う上で覚えておきたいStream API

ストリームAPIとは コレクション(集合体)をより扱いやすくするためのものです。使用する流れとして以下の通りです。 コレクションからstreamを取得 streamに対して「中間操作」を実行。コレクションの中身を都合よく変換 「終端操作」で変換したコレクションの中身に対して処理を適用する ラムダ式を使用していくのでラムダ式の使い方がわからない方は以下の記事でラムダ式について学ぶことをお勧めします。 ストリームを取得するためメソッド Collectionインターフェースのstreamメソッド デフォルトメソッドとして実装されており、ListやSetといったCollectionのサブインターフェースでも利用できます。 Sample.java import java.util.List; public class Sample { public static void main(String[] args) throws Exception{ List <Integer> list = List.of(1,2,3); list.stream().forEach(System.out::println); } } 1 2 3 Arraysクラスのstreamメソッド 配列からストリームをを作るにはArrysクラスのstreamメソッドを使います。使い方としては4つのパターンがあります。 配列が参照型の場合 Sample.java import java.util.Arrays; import java.util.stream.Stream; public class Sample { public static void main(String[] args) { String[] array = {"A","B","C"}; Stream <String>stream = Arrays.stream(array); stream.forEach(System.out::println); } A B C 配列がint型の場合 Sampl.java import java.util.Arrays; import java.util.stream.IntStream; public class Sample { public static void main(String[] args) { int[] array = {1,2,3}; IntStream intStream = Arrays.stream(array); intStream.forEach(System.out::println); } } 1 2 3 配列がLong型の場合 Sample.java import java.util.Arrays; import java.util.stream.Stream; public class Sample { public static void main(String[] args) { long l = 19700820; Long[] array = {l,l,l}; Stream<Long> stream = Arrays.stream(array); stream.forEach(System.out::println); } } 配列がDouble型の場合 Sample.java import java.util.Arrays; import java.util.stream.Stream; public class Sample { public static void main(String[] args) { Double[] array = {0.1,0.2,0.3}; Stream<Double> stream = Arrays.stream(array); stream.forEach(System.out::println); } } 0.1 0.2 0.3 中間操作 filterメソッド Predicate型の引数を受け取り、ラムダ式で指定された条件に一致するかストリーム内の要素に絞り込みます。 Sample.java import java.util.List; public class Sample { public static void main(String[] args) { List <Integer> list = List.of(1,2,3,4,5,6,7); list.stream() .filter(x -> x<5) .forEach(System.out::print); } } 1234 distinctメソッド ストリーム内の要素の重複を取り除きます。 Sample.java import java.util.List; public class Sample { public static void main(String[] args) { List <Integer> list = List.of(1,2,1,4,1,6,1); list.stream() .distinct() .forEach(System.out::print); } } 1246 limitメソッド 数を絞り込むためのメソッドです。 Sample.java import java.util.List; public class Sample { public static void main(String[] args) { List <Integer> list = List.of(1,2,1,4,1,6,1); list.stream() .limit(3) .forEach(System.out::print); } } 121 skipメソッド 引数で指定した数以降の要素を取り出すメソッドです。 Sample.java import java.util.List; public class Sample { public static void main(String[] args) { List <Integer> list = List.of(1,2,1,4,1,6,1); list.stream() .skip(3) .forEach(System.out::print); } } 4161 sortedメソッド ストリーム内の要素を並び替えるメソッドです。 Sample.java import java.util.List; public class Sample { public static void main(String[] args) { List <String> list = List.of("C","D","E","A","B"); list.stream() .sorted() .forEach(System.out::print); } } ABCDE mapメソッド ストリームの要素に対して何らかの処理を加えたい時に使用するメソッドです。 Sample.java import java.util.List; public class Sample { public static void main(String[] args) { List <String> list = List.of("apple","banana","orange"); list.stream() .map(x -> x.length()) .forEach(System.out::print); } } 566 peekメソッド 終端操作までの間にどのような状態になっているか確認したい時に使用するメソッドです。 主にデバック時に使用します。 Sample.java import java.util.Arrays; import java.util.List; public class Sample { public static void main(String[] args) { List<Integer> list = Arrays.asList(1,2,3,4,5); list.stream() .peek(x -> { System.out.println("debug:" + x); }) .filter(x -> x % 2 == 0) //偶数のみ抽出 .forEach(x->{ System.out.println("value:" +x); }); } } debug:1 debug:2 value:2 debug:3 debug:4 value:4 debug:5 終端操作 forEach ストリーム内の要素をコレクショが保持している順に処理する findFirstメソッド ストリーム内の最初の要素を取り出すメソッドです。 Sample..java import java.util.Arrays; import java.util.stream.Stream; public class Sample { public static void main(String[] args) { String[] array = {"A","B","C"}; Stream<String> stream = Arrays.stream(array); stream.findFirst() .ifPresent(System.out::println); } } A reduceメソッド 値を累積的に結合していく関数を実行するためのものです。 Sample.java import java.util.List; import java.util.Optional; public class Sample { public static void main(String[] args) { List<Integer> list = List.of(1,2,3,4,5); Optional<Integer>result=list.stream().reduce((a,b) -> a + b); result.ifPresent(System.out::print); } } 15 maxメソッド 最大値を戻します。 Sample.java import java.util.List; import java.util.Optional; public class Sample { public static void main(String[] args) { List<Integer> list = List.of(1,2,3,4,5); Optional <Integer> result = list.stream().max((x,y)->{ if(x==y) return 0; if(x<y) return -1; return 1; }); result.ifPresent(System.out::println); } } 5 minメソッド 最小値を戻します。 Sample.java import java.util.List; import java.util.Optional; public class Sample { public static void main(String[] args) { List<Integer> list = List.of(1,2,3,4,5); Optional <Integer> result = list.stream().min((x,y)->{ if(x==y) return 0; if(x<y) return -1; return 1; }); result.ifPresent(System.out::println); } } 1 終端操作は1回しか呼び出せません。 2回目を呼び出すとillegalStateExceptionがスローされます。 参考 ・徹底攻略Java SE 11 Gold問題集
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Java Silver SE11 10章

Java Silver SE11 10章 黒本をもとに学んだことをアウトプットしていきます。 主に問題を解いていて、間違えた箇所もしくは知らなかった内容になります。 例外処理 例外が発生したときに、「どのように対処するか」を記述した処理のこと。 Javaではtry-catch(-finally)構文で記述する。 tryブロック:例外が発生する可能性のある処理 catchブロック:例外が発生した時の処理 finallyブロック:例外発生の有無に関わらず実行したい処理(省略可能) 例外が発生した時、tryブロックの以降の処理を実行せず、catchブロックに移行される。 ルール try-catch-fianllyの順番は変更できない、変更した場合コンパイルエラーになる tryブロックとfinallyブロックは1つのみ、catchブロックは複数記述可能 catchブロックでreturnしても、戻る前にfinallyブロックが必ず実行される 構文 try { // 例外が発生する可能性のある処理 } catch (例外クラス型 変数名) { // 例外が発生した時の処理 } try { // 例外が発生する可能性のある処理 } catch (例外クラス型 変数名) { // 例外が発生した時の処理 } finally { // 例外発生の有無に関わらず実行したい処理 } 例題 public class Main { public static void main(String[] args) { try { int array[] = {}; array[0] = 10; System.out.println("finish"); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("error"); } } } // 結果 // error 例外 プログラムが対処できるトラブルを例外という。 例外は、検査例外と非検査例外に分かれる。 検査例外 例外処理を記述したかどうかコンパイラが検査する例外のこと Exceptionクラスのサブクラスは、RuntimeExceptionとそのサブクラスを除いて、全て検査例外に該当する try-catchしているか、throws句で宣言している必要がある 非検査例外 例外処理を記述したかどうかコンパイラが検査しない例外のこと Exceptionクラスのサブクラスではあるが、RuntimeExceptionとそのサブクラスは非検査例外として扱われる try-catchとthrows句はする必要がない (記述は可能) 例外クラス 問題で出題された例外クラスをまとめる。 例外クラス 内容 ArrayIndexOutOfBoundsException 配列の要素外に対するアクセス StringOutOfBoundsException 文字列の範囲外に対するアクセス IndexOutOfBoundsException 配列や文字列、コレクションの範囲外であることを表す。また上記2つのスーパークラスに該当する NullPointerException nullに対してメソッドを呼び出そうとしている ClassCastException 継承関係や実現関係にないクラスをキャストしようとしている エラー プログラムが対処できない事態をエラーという。 プログラムからは回復不可能なトラブルが発生したことを指している。 例:メモリ不足、読み書きの権限がない、ネットワーク接続できない など Errorクラスがあり、主にサブクラスが使用される OutOfMemoryError, NoClassDefFoundError, StackOverflowError など エラーに分類されるには、Errorを継承する必要がある try-catch, throwsを宣言する必要はない プログラムで対処することを求められていないから キャッチして処理をすることは可能 その他 例題 例題1 public class Main { public static void main(String[] args) { System.out.println(test(null)); } private static String test(Object obj) { try { System.out.println(obj.toString()); } catch (NullPointerException e) { return "A"; } finally { System.out.println("B"); } return "C"; } } // 結果 // B A // catchブロックでreturnしても、戻る前にfinallyブロックが必ず実行される 例題2 public class Main { public static void main(String[] args) { try { try { String[]array = {"A", "B", "C"}; System.out.println(array[3]); } catch (ArrayIndexOutOfBoundsException e) { System.out.println("D"); } finally { System.out.println("E"); } } catch (ArrayIndexOutOfBoundsException e) { System.out.println("F"); } finally { System.out.println("G"); } } } // 結果 // D E G
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaを使用してExcelの図形を追加、読み取り、削除する方法

この記事では、次のようなJavaプログラムを使用してExcelで図形(グラフィックス)を操作する方法を紹介します。 1.図形を追加します(図形の種類/位置/サイズの設定、図形の塗りつぶし(単色/グラデーションの色/テクスチャ/画像の塗りつぶし)、図形の表示または非表示、図形の傾斜角度、図形へのテキストの追加、図形の影など) 2.図形からテキストと画像を抽出します 3.図形を削除します(指定された図形またはすべての図形を削除します) ツール:Spire.XLS for Java Jarファイルの取得とインポート:公式WebサイトまたはMavenからダウンロードしてインポートします。インポート効果は次のとおりです。 Javaコード一覧 【例1】図形を追加する import com.spire.xls.*; import com.spire.xls.core.IPrstGeomShape; import java.awt.*; public class AddShape { public static void main(String[] args) { //テストドキュメントをロードする Workbook wb = new Workbook(); wb.loadFromFile("test.xlsx"); //ワークシートを取得する Worksheet sheet = wb.getWorksheets().get(0); //楕円形を追加する IPrstGeomShape ellipse = sheet.getPrstGeomShapes().addPrstGeomShape(3,4,125,200,PrstGeomShapeType.Ellipse); ellipse.getFill().setFillType(ShapeFillType.SolidColor);//単色塗りつぶし ellipse.getFill().setForeColor(new Color(180,125,208)); ellipse.setRotation(30);//図形の回転角度 ellipse.setText("楕円形にテキストを追加する");//図形にテキストを追加する //星の図形を追加する IPrstGeomShape star = sheet.getPrstGeomShapes().addPrstGeomShape(3,8,180,200,PrstGeomShapeType.Star5); star.getFill().setFillType(ShapeFillType.Gradient);//グラデーション塗りつぶし star.getFill().setGradientColorType(GradientColorType.Preset); star.getFill().setForeColor(Color.orange); //雲の図形を追加する IPrstGeomShape cloud = sheet.getPrstGeomShapes().addPrstGeomShape(12,4,175,200,PrstGeomShapeType.Cloud); cloud.getFill().setFillType(ShapeFillType.Texture);//テクスチャ塗りつぶし cloud.getFill().setTexture(GradientTextureType.WhiteMarble); cloud.setVisible(true);//図形が表示されるかどうかを設定する //クラウドシャドウ効果を設定する cloud.getShadow().setAngle(90); cloud.getShadow().setDistance(10); cloud.getShadow().setSize(100); cloud.getShadow().setColor(Color.GRAY); cloud.getShadow().setBlur(30); cloud.getShadow().setTransparency(1); cloud.getShadow().hasCustomStyle(); //長方形を追加する IPrstGeomShape rect = sheet.getPrstGeomShapes().addPrstGeomShape(15,8,125,200,PrstGeomShapeType.Rect); rect.getFill().customPicture("tp.png");//画像の塗りつぶしを読み込む rect.setName("Shape4");//図形を命名する //ドキュメントを保存する wb.saveToFile("AddShape.xlsx"); wb.dispose(); } } 追加した効果: 【例2】図形の中の文字や絵を読む import com.spire.xls.*; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; public class Extract { public static void main(String[] args) throws IOException { //ドキュメントをロードする Workbook wb = new Workbook(); wb.loadFromFile("C:\\Users\\Administrator\\IdeaProjects\\TEST\\AddShape.xlsx"); //ワークシートを取得する Worksheet sheet = wb.getWorksheets().get(0); //指定された図形のテキストを取得する String text = sheet.getPrstGeomShapes().get(0).getText(); System.out.println(text); //指定された形で写真を取得する BufferedImage image = sheet.getPrstGeomShapes().get(3).getFill().getPicture(); ImageIO.write(image,"png",new File("ExtractedImage.png")); } } 文字や絵を読む結果: 【例3】図形を削除する import com.spire.xls.*; public class RemoveShape { public static void main(String[] args) { //ドキュメントをロードする Workbook wb = new Workbook(); wb.loadFromFile("C:\\Users\\Administrator\\IdeaProjects\\TEST\\AddShape.xlsx"); //ワークシートを取得する Worksheet sheet = wb.getWorksheets().get(0); //指定された図形を取得し、削除する //sheet.getPrstGeomShapes().get(1).remove();//インデックス値による取得と削除する // sheet.getPrstGeomShapes().get("Shape4").remove();//形状名で取得および削除する //すべてのグラフィックを削除する for (int i = sheet.getPrstGeomShapes().getCount()-1; i >= 0; i--) { sheet.getPrstGeomShapes().get(i).remove(); } //ドキュメントを保存する wb.saveToFile("RemoveShape.xlsx"); wb.dispose(); } } プログラムを実行した後、図形の削除効果を確認できます。 今回の図形を追加、読み取り、削除する方法はここまででした、最後まで読んでいただきありがとうございます。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JNAでショートカットを作ってみた

これまでのあらすじ いまさらJavaでGUIやっているのだけれど、アプリ上からショートカットを作れっていわれた。ぐぐるさんに聞いてもあまり参考になる情報がない。まじかめんどいぞ。 ご注意 以下のコードはJNA Version 5.10.0 を利用したものとなっています。 以下のテキストは9割がた与太なので、方法だけ知りたい場合にはページの最後までスクロールしてください。 Javaじゃなかったらどうやって作るのん? どうやらプログラム上からショートカットを作るには Windows Script Host なるものを使うらしい。 shortcut.vbs ' シェルオブジェクトを取得して Set shell = WScript.CreateObject("WScript.Shell") ' ファイルの場所指定してショートカットオブジェクト生成して Set shortCut = shell.CreateShortcut(shell.SpecialFolders("Desktop") + "\cmd.lnk") ' リンク先(今回はコマンドプロンプト)を設定して Set systemEnv = shell.Environment("SYSTEM") shortCut.TargetPath = systemEnv("COMSPEC") ' 保存 shortCut.Save() なるほどこれをJNAで再現してやればいいのだな。 JNAの予習 JNAのドキュメント、Platform Library -> COM support あたりが今回の対象っぽい。 なるほど、COMBindingBaseObject を介して oleMethod をコールしろと。 しっかし投げやりだなこのドキュメント。JavaDocもおざなりだし。テストコードもサンプルコードもみあたらないし。 もう一つ方法があるみたいなんだけれど、今回そっちは断念。 とりあえず最低限の実装 WshShellTest.java public class WshShellTest extends COMBindingBaseObject { public WshShellTest() { super("WScript.Shell", false); } public String getSpecialFolder(String name) { VARIANT.ByReference result = new VARIANT.ByReference(); this.oleMethod(OleAuto.DISPATCH_PROPERTYGET, result, "SpecialFolders", new VARIANT(name)); return result.getValue().toString(); } public WshShortcut createShortcut(String path) { VARIANT.ByReference result = new VARIANT.ByReference(); this.oleMethod(OleAuto.DISPATCH_METHOD, result, "CreateShortcut", new VARIANT(path)); return new WshShortcut((IDispatch) result.getValue()); } public class WshShortcut extends COMBindingBaseObject { private WshShortcut(IDispatch iDispatch) { super(iDispatch); } public void setTargetPath(String path) { this.oleMethod(OleAuto.DISPATCH_PROPERTYPUT, null, "TargetPath", new VARIANT(path)); } public void save() { this.oleMethod(OleAuto.DISPATCH_METHOD, null, "Save"); } } public static void main(String[] args) { // COMライブラリの初期化 Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); // シェルオブジェクトを取得して WshShellTest shell = new WshShellTest(); // ファイルの場所指定してショートカットオブジェクト生成して WshShortcut shortcut = shell.createShortcut(shell.getSpecialFolder("Desktop") + "\\cmd.lnk"); // リンク先(今回はコマンドプロンプト)を設定して shortcut.setTargetPath(System.getenv("COMSPEC")); // 保存 shortcut.save(); // 解放するっぽい shell.release(); // COMライブラリの初期化解除 Ole32.INSTANCE.CoUninitialize(); } } COMBindingBaseObject::oleMethod はJNAのドキュメントのとおりに呼び出そうとするとDeprecated食らいます。 COMライブラリの初期化(CoInitializeEx)はドキュメントのどこにも書いていなかったのですが、呼んどかないと例外で怒られます。 MSのドキュメントによるとCoUninitializeも必須みたいな感じなんですが、数少ないJNAのサンプルをぐぐっても呼んでる例が見つからないので、本当に必要かどうかは不明。 COMBindingBaseObject::release も必要かどうかはわからないんだけれど、お行儀てきに呼んでおいた感じです。 まあ、なにはともあれ、どうにかショートカットができました。 一応、完成版のコードなど WshShortcutの他のプロパティへのアクセスも記述した私なりの完成版。せっかく書いたので。 WshShellSample.java WshShellSample.java package yamane; import java.io.IOException; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Ole32; import com.sun.jna.platform.win32.OleAuto; import com.sun.jna.platform.win32.Variant.VARIANT; import com.sun.jna.platform.win32.COM.COMBindingBaseObject; import com.sun.jna.platform.win32.COM.IDispatch; /** * WshShell オブジェクト */ public class WshShellSample extends COMBindingBaseObject { public WshShellSample() { super("WScript.Shell", false); } /** * SpecialFolders プロパティ(コレクション)から特定の要素を取得します。 * @param name 要素名 * @return フォルダーのパス */ public String getSpecialFolder(String name) { VARIANT.ByReference result = new VARIANT.ByReference(); this.oleMethod(OleAuto.DISPATCH_PROPERTYGET, result, "SpecialFolders", new VARIANT(name)); return result.getValue().toString(); } /** * ショートカットオブジェクトを生成します。 * @param path 生成するショートカットのパス */ public WshShortcut createShortcut(String path) { VARIANT.ByReference result = new VARIANT.ByReference(); this.oleMethod(OleAuto.DISPATCH_METHOD, result, "CreateShortcut", new VARIANT(path)); return new WshShortcut((IDispatch) result.getValue()); } /** * WshShortcutオブジェクト */ public class WshShortcut extends COMBindingBaseObject { private WshShortcut(IDispatch iDispatch) { super(iDispatch); } /** * ショートカットの実行可能ファイルへのパスを設定します。 * @param path ショートカットの実行可能ファイルへのパス */ public void setTargetPath(String path) { this.oleMethod(OleAuto.DISPATCH_PROPERTYPUT, null, "TargetPath", new VARIANT(path)); } /** * ショートカット実行時の引数を設定します。 * @param ショートカット実行時の引数 */ public void setArguments(String args) { this.oleMethod(OleAuto.DISPATCH_PROPERTYPUT, null, "Arguments", new VARIANT(args)); } /** * ショートカットの作業ディレクトリを設定します。 * @param 作業ディレクトリのパス */ public void setWorkingDirectory(String dir) { this.oleMethod(OleAuto.DISPATCH_PROPERTYPUT, null, "WorkingDirectory", new VARIANT(dir)); } /** * アイコンをショートカットに割り当てます。 * 絶対パスと、アイコンに関連付けられているインデックスを含める必要があります。 * @param アイコンの格納場所を示すパス+インデックス */ public void setIconLocation(String path) { this.oleMethod(OleAuto.DISPATCH_PROPERTYPUT, null, "IconLocation", new VARIANT(path)); } /** * ショートカットに割り当てるキーの組み合わせを設定します。 * @param keys キーの組み合わせを表す文字列 */ public void setHotkey(String keys) { this.oleMethod(OleAuto.DISPATCH_PROPERTYPUT, null, "Hotkey", new VARIANT(keys)); } /** * ウィンドウ スタイルをショートカットに割り当てます。 * 1. ウィンドウをアクティブにして表示 * 3. ウィンドウを最大化して表示 * 7. ウィンドウを最小化して表示 * @param style キーの組み合わせを表す文字列 */ public void setWindowStyle(Integer style) { this.oleMethod(OleAuto.DISPATCH_PROPERTYPUT, null, "WindowStyle", new VARIANT(style)); } /** * ショートカットの説明文(コメント)を設定します。 * @param text 説明文(コメント) */ public void setDescription(String text) { this.oleMethod(OleAuto.DISPATCH_PROPERTYPUT, null, "Description", new VARIANT(text)); } /** * ショートカットオブジェクトを保存します。 */ public void save() { this.oleMethod(OleAuto.DISPATCH_METHOD, null, "Save"); } } /** * 使い方 * @param args いらない */ public static void main(String[] args) throws IOException { // COMライブラリの初期化 Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); // シェルオブジェクトを取得 WshShellSample shell = new WshShellSample(); // ファイルの場所指定してショートカットオブジェクト生成 WshShortcut shortcut = shell.createShortcut(shell.getSpecialFolder("Desktop") + "\\cmd.lnk"); // 引数 shortcut.setArguments("/?"); // 作業ディレクトリ shortcut.setWorkingDirectory(shell.getSpecialFolder("MyDocuments")); // リンク先(今回はコマンドプロンプト)を設定 shortcut.setTargetPath(System.getenv("COMSPEC")); // 最大化 shortcut.setWindowStyle(3); // ホットキー shortcut.setHotkey("CTRL+SHIFT+F"); // コメント shortcut.setDescription("ショートカットサンプル"); // アイコン shortcut.setIconLocation("C:\\Windows\\System32\\calc.exe, 0"); // 保存 shortcut.save(); // 解放するっぽい shell.release(); // COMライブラリの初期化解除 Ole32.INSTANCE.CoUninitialize(); } } 参考ページ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Windows環境のJavaでショートカットを作ってみた(もしくはJNAでCOMバインディングしてみた)

これまでのあらすじ いまさらJavaでGUIやっているのだけれど、アプリ上からショートカットを作れっていわれた。ぐぐるさんに聞いてもあまり参考になる情報がない。まじかめんどいぞ。 ご注意 コードは JNA Version 5.10.0 を利用したものとなっています。 以下のテキストは9割がた与太なので、適当に読み流してください。 Javaじゃなかったらどうやって作るのん? どうやらプログラム上からショートカットを作るには Windows Script Host なるものを使うらしい。 shortcut.vbs ' シェルオブジェクトを取得して Set shell = WScript.CreateObject("WScript.Shell") ' ファイルの場所指定してショートカットオブジェクト生成して Set shortCut = shell.CreateShortcut(shell.SpecialFolders("Desktop") + "\cmd.lnk") ' リンク先(今回はコマンドプロンプト)を設定して Set systemEnv = shell.Environment("SYSTEM") shortCut.TargetPath = systemEnv("COMSPEC") ' 保存 shortCut.Save() なるほどこれをJNAで再現してやればいいのだな。 JNAの予習 JNAのドキュメントにある、Platform Library -> COM support あたりで実現できそうです。 ドキュメントによると方法は2つ。 COMBindingBaseObject を継承したクラスをつくって oleMethod をコールする方法がひとつめ。 もうひとつはcom.sun.jna.platform.win32.COM.utilパッケージのやつだぜってだけ書いてある。 こんな投げやりなドキュメントなんですが、jna/contrib/msoffice/ の方にMSOfficeを例にしたサンプルコードがあるらしいので、そっちを見ながら進めることにします。 コードジェネレーターなんてものもあるみたいです。そのうち試してみたいけれど、今回は除外。 ひとつめのアプローチ デモのコードがドキュメントに書いてあったのと違うじゃん。 COMBindingBaseObject を継承したクラスで oleMethod 呼ぶ方法でも実現はできるんですが、もう一段階手続きを簡略化してくれるクラスが準備されています。なのでこの COMLateBindingObject を使っていきます。 getProperty/setProperty でプロパティの読み書きして invoke でメソッドを呼ぶ。 昔ながらのJavaっていう感じのアプローチだけれど、JNAっぽくはないなあ(個人的な見解)。 WshShell.java public class WshShell extends COMLateBindingObject { public WshShellTest() { super("WScript.Shell", false); } public String getSpecialFolder(String name) { VARIANT val = invoke("SpecialFolders", new VARIANT(name)); return val.getValue().toString(); } public WshShortcut createShortcut(String path) { VARIANT val = invoke("CreateShortcut", new VARIANT(path)); return new WshShortcut((IDispatch) val.getValue()); } public class WshShortcut extends COMLateBindingObject { private WshShortcut(IDispatch iDispatch) { super(iDispatch); } public void setTargetPath(String path) { setProperty("TargetPath", path); } public void save() { invoke("Save"); } } public static void main(String[] args) { // COMライブラリの初期化 Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); try { // シェルオブジェクトを取得して WshShell shell = new WshShell(); // ファイルの場所指定してショートカットオブジェクト生成して WshShortcut shortcut = shell.createShortcut(shell.getSpecialFolder("Desktop") + "\\cmd.lnk"); // リンク先(今回はコマンドプロンプト)を設定して shortcut.setTargetPath(System.getenv("COMSPEC")); // 保存 shortcut.save(); } finally { // COMライブラリの初期化解除 Ole32.INSTANCE.CoUninitialize(); } } } ふたつめのアプローチ デモのコードを読んでみたところ、どうやらインターフェイスにアノテーションを定義してマッピングをするらしい。 実際にはこんな感じです。こっちのほうがJNAっぽいなあ(個人的な見解)。 IWshShell.java @ComObject(progId = "WScript.Shell") public interface IWshShell { @ComProperty(name = "SpecialFolders") String SpecialFolders(String name); @ComMethod(name = "CreateShortcut") IDispatch CreateShortcut(String path); public interface IWshShortcut { @ComProperty(name = "TargetPath") void TargetPath(String path); @ComMethod(name = "Save") void Save(); } public static void main(String[] args) { // COMライブラリの初期化 Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); try { // ファクトリを準備して ObjectFactory factory = new ObjectFactory(); // シェルオブジェクトを取得して IWshShell shell = factory.createObject(IWshShell.class); // ファイルの場所指定してショートカットオブジェクト生成して String linkpath = shell.SpecialFolders("Desktop") + "\\cmd.lnk"; IDispatch dispatch = shell.CreateShortcut(linkpath); IWshShortcut shortcut = factory.createProxy(IWshShortcut.class, dispatch); // リンク先(今回はコマンドプロンプト)を設定して shortcut.TargetPath(System.getenv("COMSPEC")); // 保存 shortcut.Save(); }finally { // COMライブラリの初期化解除 Ole32.INSTANCE.CoUninitialize(); } } } まあ、なにはともあれ、どちらの方法でも、簡単にショートカットができました。すごい。 一応、完成版のコードなど WshShortcutの他のプロパティへのアクセスも記述したやつを載せておきます。せっかく書いたので。 WshShell.java(ひとつめ) WshShell.java package yamane; import java.io.IOException; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Ole32; import com.sun.jna.platform.win32.Variant.VARIANT; import com.sun.jna.platform.win32.COM.COMLateBindingObject; import com.sun.jna.platform.win32.COM.IDispatch; /** * WshShell オブジェクト */ public class WshShell extends COMLateBindingObject { public WshShell() { super("WScript.Shell", false); } /** * SpecialFolders プロパティ(コレクション)から特定の要素を取得します。 * @param name 要素名 * @return フォルダーのパス */ public String getSpecialFolder(String name) { VARIANT val = invoke("SpecialFolders", new VARIANT(name)); return val.getValue().toString(); } /** * ショートカットオブジェクトを生成します。 * @param path 生成するショートカットのパス */ public WshShortcut createShortcut(String path) { VARIANT val = invoke("CreateShortcut", new VARIANT(path)); return new WshShortcut((IDispatch) val.getValue()); } /** * WshShortcutオブジェクト */ public class WshShortcut extends COMLateBindingObject { private WshShortcut(IDispatch iDispatch) { super(iDispatch); } /** * ショートカットの実行可能ファイルへのパスを設定します。 * @param path ショートカットの実行可能ファイルへのパス */ public void setTargetPath(String path) { setProperty("TargetPath", path); } /** * ショートカット実行時の引数を設定します。 * @param args ショートカット実行時の引数 */ public void setArguments(String args) { setProperty("Arguments", args); } /** * ショートカットの作業ディレクトリを設定します。 * @param dir 作業ディレクトリのパス */ public void setWorkingDirectory(String dir) { setProperty("WorkingDirectory", dir); } /** * アイコンをショートカットに割り当てます。 * 絶対パスと、アイコンに関連付けられているインデックスを含める必要があります。 * @param path アイコンの格納場所を示すパス+インデックス */ public void setIconLocation(String path) { setProperty("IconLocation", path); } /** * ショートカットに割り当てるキーの組み合わせを設定します。 * @param keys キーの組み合わせを表す文字列 */ public void setHotkey(String keys) { setProperty("Hotkey", keys); } /** * ウィンドウ スタイルをショートカットに割り当てます。 * 1. ウィンドウをアクティブにして表示 * 3. ウィンドウを最大化して表示 * 7. ウィンドウを最小化して表示 * @param style キーの組み合わせを表す文字列 */ public void setWindowStyle(Integer style) { setProperty("WindowStyle", style); } /** * ショートカットの説明文(コメント)を設定します。 * @param text 説明文(コメント) */ public void setDescription(String text) { setProperty("Description", text); } /** * ショートカットオブジェクトを保存します。 */ public void save() { invoke("Save"); } } /** * 使い方 * @param args 参照してない */ public static void main(String[] args) throws IOException { // COMライブラリの初期化 Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); try { // シェルオブジェクトを取得 WshShell shell = new WshShell(); // ファイルの場所指定してショートカットオブジェクト生成 WshShortcut shortcut = shell.createShortcut(shell.getSpecialFolder("Desktop") + "\\cmd.lnk"); // 引数 shortcut.setArguments("/?"); // 作業ディレクトリ shortcut.setWorkingDirectory(shell.getSpecialFolder("MyDocuments")); // リンク先(今回はコマンドプロンプト)を設定 shortcut.setTargetPath(System.getenv("COMSPEC")); // 最大化 shortcut.setWindowStyle(3); // ホットキー shortcut.setHotkey("CTRL+SHIFT+F"); // コメント shortcut.setDescription("ショートカットサンプル"); // アイコン shortcut.setIconLocation("C:\\Windows\\System32\\calc.exe, 0"); // 保存 shortcut.save(); } finally { // COMライブラリの初期化解除 Ole32.INSTANCE.CoUninitialize(); } } } IWshShell.java(ふたつめ) IWshShell.java package yamane; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.Ole32; import com.sun.jna.platform.win32.COM.IDispatch; import com.sun.jna.platform.win32.COM.util.ObjectFactory; import com.sun.jna.platform.win32.COM.util.annotation.ComMethod; import com.sun.jna.platform.win32.COM.util.annotation.ComObject; import com.sun.jna.platform.win32.COM.util.annotation.ComProperty; /** * WshShell インターフェイス */ @ComObject(progId = "WScript.Shell") public interface IWshShell { /** * SpecialFolders プロパティ(コレクション)から特定の要素を取得します。 * @param name 要素名 * @return フォルダーのパス */ @ComProperty(name = "SpecialFolders") String SpecialFolders(String name); /** * ショートカットオブジェクトを生成します。 * @param path 生成するショートカットのパス */ @ComMethod(name = "CreateShortcut") IDispatch CreateShortcut(String path); /** * WshShortcut インターフェイス */ public interface IWshShortcut { /** * ショートカットの実行可能ファイルへのパスを設定します。 * @param path ショートカットの実行可能ファイルへのパス */ @ComProperty(name = "TargetPath") void TargetPath(String path); /** * ショートカット実行時の引数を設定します。 * @param args ショートカット実行時の引数 */ @ComProperty(name = "Arguments") void Arguments(String args); /** * ショートカットの作業ディレクトリを設定します。 * @param dir 作業ディレクトリのパス */ @ComProperty(name = "WorkingDirectory") void WorkingDirectory(String dir); /** * アイコンをショートカットに割り当てます。 * 絶対パスと、アイコンに関連付けられているインデックスを含める必要があります。 * @param path アイコンの格納場所を示すパス+インデックス */ @ComProperty(name = "IconLocation") void IconLocation(String path); /** * ショートカットに割り当てるキーの組み合わせを設定します。 * @param keys キーの組み合わせを表す文字列 */ @ComProperty(name = "Hotkey") void Hotkey(String keys); /** * ウィンドウ スタイルをショートカットに割り当てます。 * 1. ウィンドウをアクティブにして表示 * 3. ウィンドウを最大化して表示 * 7. ウィンドウを最小化して表示 * @param style キーの組み合わせを表す文字列 */ @ComProperty(name = "WindowStyle") void WindowStyle(Integer style); /** * ショートカットの説明文(コメント)を設定します。 * @param text 説明文(コメント) */ @ComProperty(name = "Description") void Description(String text); /** * ショートカットオブジェクトを保存します。 */ @ComMethod(name = "Save") void Save(); } public static void main(String[] args) { // COMライブラリの初期化 Ole32.INSTANCE.CoInitializeEx(Pointer.NULL, Ole32.COINIT_MULTITHREADED); try { // ファクトリを準備 ObjectFactory factory = new ObjectFactory(); // シェルオブジェクトを取得 IWshShell shell = factory.createObject(IWshShell.class); // ファイルの場所指定してショートカットオブジェクト生成 String linkpath = shell.SpecialFolders("Desktop") + "\\cmd.lnk"; IDispatch dispatch = shell.CreateShortcut(linkpath); IWshShortcut shortcut = factory.createProxy(IWshShortcut.class, dispatch); // 引数 shortcut.Arguments("/?"); // 作業ディレクトリ shortcut.WorkingDirectory(shell.SpecialFolders("MyDocuments")); // リンク先(今回はコマンドプロンプト)を設定 shortcut.TargetPath(System.getenv("COMSPEC")); // 最大化 shortcut.WindowStyle(3); // ホットキー shortcut.Hotkey("CTRL+SHIFT+F"); // コメント shortcut.Description("ショートカットサンプル"); // アイコン shortcut.IconLocation("C:\\Windows\\System32\\calc.exe, 0"); // 保存 shortcut.Save(); }finally { // COMライブラリの初期化解除 Ole32.INSTANCE.CoUninitialize(); } } } 参考ページ
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Javaのクラスパス(classpath)の設定方法

クラスパスを設定する方法は下記の3つあります。 1 コマンドラインで指定する java -classpath C:\program ';'(セミコロン)区切りで複数のクラスパスを設定できます。 javaコマンドでは'-classpath'を'-cp'に省略可能です。 java -cp C:\program1;C:\program2 2 OSの環境変数に設定する Windows set CLASSPATH=C:\program1;C:\program2 macOS export CLASSPATH=~/program1;~/program2 linux setenv CLASSPATH=/home/program 3 デフォルト(指定しない場合) カレントディレクトリになります。 'C:\program\sample'で実行した場合は'C:\program\sample'がクラスパスになります。 参考文献 https://docs.oracle.com/javase/jp/1.5.0/tooldocs/windows/classpath.html スッキリわかるJava入門 第3版 スッキリわかるシリーズ https://www.k-cube.co.jp/wakaba/server/class_path.html
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

クラス変数の持ち主

これは何? クラス変数の持ち主が、 python と ruby で違うことに気がついたので、他の言語どうなってるんだろと思って調査したのでその記録。ジェネリクスとテンプレートを交えながら。 ruby の場合 ruby class B def set_v(x); @@v=x; end def v(); @@v; end end class C < B;end class D < B;end c = C.new.set_v("C#set_v") d = D.new.set_v("D#set_v") p [ C.new.v, D.new.v ] #=> ["D#set_v", "D#set_v"] クラス変数 @@v の持ち主は、 class B 。 C 経由で変更しても、 D から見た @@v の値が変わる。 Python3 の場合 python3 class B: v = "in-class-def" @classmethod def setV(cls,x): cls.v = x class C(B):... class D(B):... print(B().v, C().v, D().v) #=> in-class-def in-class-def in-class-def C().setV("C.setV") D().setV("D.setV") print(B().v, C().v, D().v) #=> in-class-def C.setV D.setV Python のクラス変数はcls.v のようにするので、v の持ち主は cls で指定される型。なので、クラス変数を参照しているメソッドが書かれているクラスとは関係がない。 最初の print 内にある C.v なんかは、C の基底クラスを見に行っている。 わりと珍しい作戦だと思う。 この作戦だと、深い継承ツリーで偶然同じ名前を使ってしまったために大惨事、みたいなことが起きると思う。 ※ 初出時間違ったことを書いてましたすいません。 Java の場合 Generics なしで まずは Generics なしで。 java import java.io.*; class B{ public static String s; } class C1 extends B {}; class C2 extends B {}; class Foo { public static void main (String[] args) throws java.lang.Exception { C1.s = "c1"; C2.s = "c2"; System.out.printf( "C1.s=%s C2.s=%s\n", C1.s, C2.s ); //=> C1.s=c2 C2.s=c2 } } ここは ruby と同じく、クラス変数の持ち主は、その変数が定義されたクラス。 Generics を使うと Generics を使って、型引数を変えてみると java import java.io.*; class B<T>{ public static String s; T dummy; } class C1 extends B<Integer> {}; class C2 extends B<Long> {}; class Foo { public static void main (String[] args) throws java.lang.Exception { C1.s = "c1"; C2.s = "c2"; System.out.printf( "C1.s=%s C2.s=%s\n", C1.s, C2.s ); //=> C1.s=c2 C2.s=c2 } } 異なる型引数でもクラス変数は同じだということがわかる。 なので、型引数の型を static 変数には使えない。なるほどこれが型消去かという感じ。 C++ の場合 テンプレートなし まずはテンプレートなしで。 c++17 #include <iostream> #include <string> struct B{ static inline std::string s; }; struct C1 : public B{}; struct C2 : public B{}; int main(){ C1{}.s = "c1"; C2{}.s = "c2"; std::cout << C1{}.s << " " << C2{}.s << std::endl; //=> c2 c2 return 0; } ruby, Java と同様、static変数(クラス変数) の持ち主は、その変数が定義されたクラス。 テンプレートあり テンプレートクラスの場合。 c++17 #include <iostream> #include <string> template<int n> struct B{ static inline std::string s; }; struct C1 : public B<0>{}; struct C2 : public B<1>{}; int main(){ C1{}.s = "c1"; C2{}.s = "c2"; std::cout << C1{}.s << " " << C2{}.s << std::endl; //=> c1 c2 return 0; } テンプレート引数が異なるクラスは別クラスになる。なので、Java と違って static 変数の型にテンプレート引数が使える。 C# の場合 Generics なし C# using System; class B{ public static String s; } class C1 : B{}; class C2 : B{}; public class Test { public static void Main() { C1.s = "c1"; C2.s = "c2"; Console.WriteLine( "C1.s={0} C2.s={1}", C1.s, C2.s); //=> C1.s=c2 C2.s=c2 } } まあそうだよね。Java と同じ。 Generics あり Generics ありだと C# using System; class B<T>{ public static String s;} class C1 : B<int>{}; class C2 : B<long>{}; public class Test { public static void Main() { C1.s = "c1"; C2.s = "c2"; Console.WriteLine( "C1.s={0} C2.s={1}", C1.s, C2.s); //=> C1.s=c1 C2.s=c2 } } Java とは異なる。 まとめ ひとくちに「クラス変数」というけれど、言語によって意味が違うので要注意。 Python はたぶん異端で、明示されているレシーバがそのクラス変数の持ち主。基底クラス内の cls.class_var は、 cls が異なれば異なる変数。cls 指すクラスの別のスーパークラスで同名のクラス変数を使うと同じ変数となる。 ruby, Java, C++, C# は、クラス変数が定義されたクラスがクラス変数の持ち主。 Generics / template を使う場合。 型引数が違っても同じクラス、というのが Java の立場。 template 引数 / 型引数が違うクラスは別のクラス、というのが C++ と C# の立場。 他に試すべき言語あるかなぁ。 余談 今は亡き J# でジェネリクス使ったらどうなるんだろ。 そもそも J# でジェネリクス使えるかどうかも知らないけど。 以下、投稿の翌日に追加。 Python と ruby のクラス変数の気持ち悪いところ コメントいただいたり他に試したりして、何が気持ち悪いのかわかってきた。 Python の場合 シンプルな例 まずは普通っぽい例。 Python3 class B:v="B0" class C(B):... class D(B):... # [1] print(B.v, C.v, D.v) #=> "B0 B0 B0" # [2] C.v = "C0" print(B.v, C.v, D.v) #=> "B0 C0 B0" [1] の print 内の C.v は、 B の v のことで、 [2] の C.v="C0" の C.v は、 C の v のことなので、C.v="C0" は D.v で得られる値には影響を与えない。 同じ C.v という式だけど、参照するときと代入の左辺になるときで意味が違うのがわかりにくい。とはいえ、オブジェクト指向とはそのようなものだとも思う。 わかりにくい現象 このわかりにくさと Python の += なんかのわかりにくさが合体して、こんなわかりにくいことが起こる。 Python3 class B: a=["a"] b=["b"] c = "c" d = "d" class C(B):... print(repr([B.a, B.b, B.c, B.d])) #=> [['a'], ['b'], 'c', 'd'] C.a += ["+="] C.b = C.b + ["+"] C.c += "+=" C.d = C.d + "+" print(repr([B.a, B.b, B.c, B.d])) #> [['a', '+='], ['b'], 'c', 'd'] リストの += は新たなオブジェクトは作られないので基底クラスの B.a が書き換わり。 文字列の += は新たなオブジェクトが作られるので代入と同じことになるから B.c は書き換わらない。 かなり気持ち悪い動作だと思うけど、なんでそうなるのかは説明できる感じ。 ruby の場合 シンプルな例 一方 ruby の普通っぽい例を実行すると。 ruby class B def set_vb(x); @@v=x; end def vb(); @@v; end end class C < B def set_vc(x); @@v=x; end def vc(); @@v; end end class D < B def vd(); @@v; end end B.new.set_vb("B0") p [B.new.vb, C.new.vc, D.new.vd] #=> ["B0", "B0", "B0"] C.new.set_vc("C0") p [B.new.vb, C.new.vc, D.new.vd] #=> ["C0", "C0", "C0"] 上記の通り Python とは違う。 class C 内の @@v を書き換えると class B 内で定義された @@v が書き換わる。なので、C の派生クラスでも基底スラスでもない D から見える @@v にも影響を与える。 ここだけ見るとそれはそれでありかなと思うけれど。 わかりにくい現象 ならば、派生で @@v を作ってから基底で @@v に代入すると ruby class B def set_vb(x); @@v=x; end def vb(); (defined? @@v) ? @@v : :undefined; end end class C < B def set_vc(x); @@v=x; end def vc(); (defined? @@v) ? @@v : :undefined; end end # [1] C.new.set_vc("C0") p [B.new.vb, C.new.vc] #=> [:undefined, "C0"] # [2] B.new.set_vb("B0") p [B.new.vb, C.new.vc] # ruby 2.7.5 では、["B0", "B0"] # ruby 3.1.0 では例外。class variable @@v of C is overtaken by B なんと、 ruby 3.1 では例外。 2.7.5 では、基底クラスの @@v を書き換えると派生クラスの @@v が書き換わる。難しい。難しすぎるので例外にしたんだろうと思う。 あるいは。 以下を実行すると。 ruby module B @@x = :bx def self.b() [ (defined? @@x) ? @@x : :undef, (defined? @@y) ? @@y : :undef, (defined? @@z) ? @@z : :undef] end end module C @@y = :cy def self.c() [ (defined? @@x) ? @@x : :undef, (defined? @@y) ? @@y : :undef, (defined? @@z) ? @@z : :undef] end end class D include B, C @@x = :dx @@y = :dy @@z = :dz def self.d(); [ (defined? @@x) ? @@x : :undef, (defined? @@y) ? @@y : :undef, (defined? @@z) ? @@z : :undef] end end p [B.b, C.c, D.d] #=> [[:dx, :undef, :undef], [:undef, :dy, :undef], [:dx, :dy, :dz]] module B @@x = :bx2 @@y = :by2 @@z = :bz2 end p [B.b, C.c, D.d] # 3.0.3 だとここで例外 #=> 3.1.0: [[:bx2, :by2, :bz2], [:undef, :dy, :undef], [:bx2, :dy, :dz]] #=> 2.7.5: [[:bx2, :by2, :bz2], [:undef, :dy, :undef], [:bx2, :dy, :bz2]] module C @@x = :cx2 @@y = :cy2 @@z = :cz2 end p [B.b, C.c, D.d] #=> 3.1.0: [[:bx2, :by2, :bz2], [:cx2, :cy2, :cz2], [:bx2, :cy2, :dz]] #=> 2.7.5: [[:bx2, :by2, :bz2], [:cx2, :cy2, :cz2], [:cx2, :cy2, :cz2]] なななんと、2.7.5 でも 3.1.0 でも例外にならずに完走するが、結果が違う。 びっくりした。 3.1.0 だと完走するのに、3.0.3 だと例外。さらにびっくりした。 びっくりしたし、それぞれどんな理路でそのような出力になったり例外になったりするのかは私には説明できない。 2.7.5 と 3.1.0 で結果が違うことに気づいた時点で真剣に考えるのを放棄したということでもあるけれど。 再度まとめ Python と ruby のクラス変数は、両方ともわりと思いがけないことが起こる。 どちらも気持ち悪いけど、どちらかというとというかわりと圧倒的に ruby の方が気持ち悪い。 ruby のクラス変数はめちゃくちゃ気持ち悪いし、中の人も悩んでいるんだと思うので、継承が絡む局面での利用は避けたほうがいいと思う。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む