- 投稿日:2020-03-17T23:24:52+09:00
初心者から始めるJava、オーバーライド
はじめに
この記事は備忘録である。
参考書レベルの内容だが、本記事に掲載するコードについては、
間違えたものが中心となる。これは実際にコーディング中に間違えた部分を掲載し、自分で反省するために投稿するという目的によるもの。
また、後日にJavaSilver試験問題の勉強を兼ねて復習するため、深い部分の話はここでは触れない。環境
言語:Java11、JDK13.0.2
動作環境:Windows10オーバーライド
前回でスーパークラスとサブクラスの関係は扱った。
サブクラスにはスーパークラスのメソッドが継承されているが、同じメソッド名を使いながら別の処理を定義したいということがある。
例えばshow〇〇()
でメンバをチェックするためのメソッドを用意する場合。同じチェックするにしてもwatch〇〇(),see〇〇(),check〇〇()
と使い分けを作ることはできるだろう。だが、引数や型がそれぞれ違うとなれば覚えて使うのが非常に困難になるし、それだけコードが無駄に長くなれば可読性も落ちる。
サブクラスには、スーパークラスで実装されたものと全く同じメソッドを新たに定義することができ、オブジェクトから呼び出す場合サブクラスの方が優先される。条件としては、
1.シグニチャ(メソッド名、引数リストの型、数、順番)が同じ
2.アクセス修飾子が同じかより緩い
3.戻り値の型が原則同じ(共変戻り値を除く)サブクラスのメソッドが、スーパークラスのメンバに代わって機能することをオーバーライド(overriding)と呼ぶ。
スーパークラスでまとめて扱う
何度も書くが、サブクラスにはスーパークラスのメンバが継承されている。そしてオーバーライドでサブクラス専用の処理を書いて優先的に機能させることが出来る。
よって、同じスーパークラスの子であるサブクラスを使う際、スーパークラスのメソッドで一括して処理を記述することが出来る。arrayCatsShow.javaclass WildCat { protected int number; protected double weight; public WildCat() { number = 1; weight = 1.0; } public void getCat(int n,double w) { number = n; weight = w; System.out.println(number +"番目の猫の体重は" + weight + "kgです。"); } public void show() { System.out.println("この猫は" + number + "番目の猫です。"); System.out.println("この猫の体重は" + weight + "kgです。"); } }この
WildCat
クラスをもとに、HouseCat
とMyCat
のクラスを用意する。arrayCatsShow.javaclass HouseCat extends WildCat { public void show() { System.out.println("このイエネコは" + number + "番目です。" ); } } class MyCat extends WildCat { public void show() { System.out.println("このうちの猫は" + number + "匹目で、今の体重は" + weight + "でした。" ); } }そのうえで、個別の猫を用意する。
arrayCatsShow.javaclass arrayCatsShow { public static void main(String[] args) { WildCat[] cats = new WildCat[4]; cats[0] = new WildCat(); cats[0].getCat(1, 5.2); cats[1] = new HouseCat(); cats[1].getCat(2, 4.2); cats[2] = new MyCat(); cats[2].getCat(3, 5.6); cats[3] = new MyCat(); cats[3].getCat(4, 8.4); for(int i=0; i< cats.length; i++){ cats[i].show(); } } }スーパークラスの配列を用意してから個別に
new
したので、それぞれのshow()
定義に基づいた文章が現れる。付け足しだけしたいとき
スーパークラスの処理+サブクラスの処理 にしたい場合。
HouseCatShow.javaclass HouseCat extends WildCat { public void show() { super.show(); System.out.println("このイエネコは" + number + "番目です。" ); } }
super.メソッド名
でスーパークラスの処理をそのまま持ってこれるので、あらためてすべて書く必要はない。これはフィールドにも言えることなので、super.フィールド
でスーパークラスの値を持ってくることもできる。ところで、スーパークラスのメソッドをオーバーライドされたくないときは、
public final void show()
のようにfinal
をつければオーバーライドできなくなる。
これはクラス名とフィールドでもできるので、finalクラス
ならサブクラスの拡張不可になり、finalフィールド
ならフィールドの値が宣言時の初期化を除いて変更されない定数(constant)
になる。おわりに
共変戻り値という言葉をSilver問題集から見つけたが、近いうちにクラスライブラリを調べるつもりなので、そこでクラス間の関係をはっきりさせてから扱いたい。
参考
出来るだけ自分で変数や式を書いてコンパイルしているので、完全に引用する場合はその旨記述する。
- 投稿日:2020-03-17T13:06:45+09:00
Javaでネットワークインターフェースにアクセスする
TL;DR
- Javaでネットワークインターフェースにアクセスするには、
java.net.NetworkInterface
を使用する- ネットワークインターフェース名や、
InetAddress
、Macアドレスなどが取得可能やったことがなかったので。
環境
Ubuntu Linux 18.04 LTS上で、
$ uname -srvmpio Linux 4.18.0-25-generic #26~18.04.1-Ubuntu SMP Thu Jun 27 07:28:31 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 18.04.4 LTS Release: 18.04 Codename: bionicJava 11。
$ java --version openjdk 11.0.6 2020-01-14 OpenJDK Runtime Environment (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1) OpenJDK 64-Bit Server VM (build 11.0.6+10-post-Ubuntu-1ubuntu118.04.1, mixed mode, sharing)
java.net.NetworkInterface
java.net.NetworkInterface
を使用して、ネットワークインターフェースにアクセスします。ホストのネットワークインターフェースの情報。
$ ip a 1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever inet6 ::1/128 scope host valid_lft forever preferred_lft forever 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000 link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff inet 192.168.200.130/24 brd 192.168.200.255 scope global dynamic noprefixroute ens33 valid_lft 1737sec preferred_lft 1737sec inet6 fe80::6f10:63fc:a80e:7ea0/64 scope link noprefixroute valid_lft forever preferred_lft forever 3: br-72f78aadc23c: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff inet 172.19.0.1/16 brd 172.19.255.255 scope global br-72f78aadc23c valid_lft forever preferred_lft forever 4: br-8dd168bc4c0e: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff inet 172.18.0.1/16 brd 172.18.255.255 scope global br-8dd168bc4c0e valid_lft forever preferred_lft forever 5: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff inet 172.22.0.1/16 brd 172.22.255.255 scope global docker0 valid_lft forever preferred_lft foreverサンプルコード。
NetworkInterface.getNetworkInterfaces()
で全ネットワークインターフェースを、またネットワークインターフェース名やインデックス、InetAddress
でもネットワークインターフェースを取得することができます。
App.java
import java.io.IOException; import java.net.NetworkInterface; import java.net.SocketException; import java.util.Collections; import java.util.List; import java.util.StringJoiner; public class App { public static void main(String... args) throws SocketException { List<NetworkInterface> networkInterfaces = Collections.list(NetworkInterface.getNetworkInterfaces()); networkInterfaces.forEach(ni -> { try { System.out.printf( "name = %s, addresses = %s, mac address = %s%n", ni.getName(), Collections.list(ni.getInetAddresses()), formatBinaryToHexString(ni.getHardwareAddress()) ); } catch (IOException e) { // ignore } }); NetworkInterface ni = NetworkInterface.getByName("ens33"); System.out.printf( "name = %s, addresses = %s, mac address = %s%n", ni.getName(), Collections.list(ni.getInetAddresses()), formatBinaryToHexString(ni.getHardwareAddress()) ); } static String formatBinaryToHexString(byte[] binary) { if (binary == null) { return "[]"; } StringJoiner joiner = new StringJoiner(":"); for (byte b : binary) { joiner.add(String.format("%02x", b)); } return joiner.toString(); } }実行例。
name = docker0, addresses = [/172.22.0.1], mac address = xx:xx:xx:xx:xx:xx name = br-8dd168bc4c0e, addresses = [/172.18.0.1], mac address = xx:xx:xx:xx:xx:xx name = br-72f78aadc23c, addresses = [/172.19.0.1], mac address = xx:xx:xx:xx:xx:xx name = ens33, addresses = [/fe80:0:0:0:6f10:63fc:a80e:7ea0%ens33, /192.168.200.130], mac address = xx:xx:xx:xx:xx:xx name = lo, addresses = [/0:0:0:0:0:0:0:1%lo, /127.0.0.1], mac address = [] name = ens33, addresses = [/fe80:0:0:0:6f10:63fc:a80e:7ea0%ens33, /192.168.200.130], mac address = xx:xx:xx:xx:xx:xx
- 投稿日:2020-03-17T09:36:43+09:00
javaのstreamとかラムダ式とかFunctionとか配列とかListやMapとかをこれから実践していくつもりです
-1.お断り
勉強中の身ながら執筆しております。
下書きが10個たまってしまってこれ以上下書きが増やせないので、全然内容がないけどもう公開しちゃいます。
ここに乗っているのが最適とは限りません。
コメント等いただければ嬉しい限りです。また、各メソッドについて、所属するクラス名は
Test
とします。
また、import java.util.Arrays;
,import java.util.Stream;
をしてください。0. List⇔配列
0-1. List<Hoge> → Hoge[]
static class Hoge{String s;Hoge(String s_){s=s_;}String f(){return s;}} static Test.Hoge[] hoge_list_to_array(List<Test.Hoge> hoge_list) { //return (Hoge[]) hoge_list.toArray(); ←ダメな例 return hoge_list.toArray(Test.Hoge[]::new); } public static void main(String...args) { ArrayList<Test.Hoge> hoge_list = new ArrayList<>(Arrays.asList(new Test.Hoge[]{new Hoge("hoge1"), new Hoge("hoge2")})); System.out.println(Test.hoge_list_to_array(hoge_list).getClass().getName());//[Ltrash.Test$Hoge; Stream.of(Test.hoge_list_to_array(hoge_list)).forEach(hoge -> System.out.println(hoge.f()));//hoge1 hoge2 }0-2. Hoge[] → List<Hoge>
static ArrayList<Test.Hoge> hoge_array_to_list(Test.Hoge[] hoge_array) { //return (ArrayList<Hoge>) Arrays.asList(hoge_array); ←ダメな例 return new ArrayList<Hoge>(Arrays.asList(hoge_array)); } public static void main(String...args) { Test.Hoge[] hoge_array = new Test.Hoge[]{new Hoge("hoge1"), new Hoge("hoge2")}; System.out.println(Test.hoge_array_to_list(hoge_array).getClass().getName()); for(Test.Hoge hoge : Test.hoge_array_to_list(hoge_array))//java.util.ArrayList (型パラメータ情報は消える) System.out.println(hoge.f());//hoge1 hoge2 }1.数学的計算
1-1. 線形数学(ベクトル、行列、テンソル等)
double
等のn次元配列を階数nのテンソルとみなします。
つまりベクトルといったらdouble[]
,行列といったらdouble[][]
をイメージしてほしいわけです。1-1-1. ベクトルの絶対値を求める
static double abs(double[] vector) { return Math.sqrt(Stream.of(vector).map(comp -> comp*comp).sum()); } public static void main(String...args) { double[] vector = {3,4}; System.out.println(Test.abs(vector));//5.0 }1-1-2. ベクトルに成分を追加
例えば $\left( \begin{array}{} 1 \\ 2\end{array} \right)$ に3を追加して$\left( \begin{array}{} 1 \\ 2 \\ 3 \end{array} \right)$ とするなど。
static double[] add_comp(double[] vector, double comp) { return Stream.concat(Stream.of(vector).boxed(), Stream.of(new Double[]{comp})).mapToDouble(d->d).toArray(); } public static void main(String...args) { double[] vector = {1,2}; double comp_to_add = 3; vector = Test.add_comp(vector, comp_to_add); for(double comp : vector)System.out.println(comp);// 1.0 2.0 3.0 }
return Arrays.asList(vector, comp).toArray(double[]::new);
はコンパイル時に怒られる。
return (double[])(Object)Arrays.asList(vector, comp).toArray();
は実行時にClassCastException
となる。
Stream.concat
メソッドはプリミティブのStreamに対しては使えない。そのため一度ラップしてから、mapToDouble(d->d)
でアンラップ。1-1-3. ベクトルの加算
static double[] vectors_add(double[] vector1, double[] vector2) { final int[] inde = {0}; final int x = 0; return Stream.of(vector1).map(v1 -> v1 + vector2[inde[x]++]).toArray(); } public static void main(String...args) { double[] ds1 = {1,2,3}; double[] ds2 = {4,5,6}; for(double comp : Test.vectors_add(ds1,ds2)) System.out.println(comp);//5.0 7.0 9.0 }javaの仕様で、ラムダ式内では、ラムダ式外の変数に対して、次のことができない。
- プリミティブ型の値を変更する
- 参照型の参照先を変更する
したがって、次の例は失敗する。(コンパイルエラー)
?ラムダ式外のプリミティブ型変数indexをラムダ式内で変更しようとしたのでエラーになる例static double[] vectors_add(double[] vector1, double[] vector2) { /*final*/ int index = 0; return Stream.of(vector1).map(v1 -> v1 + vector2[index++]).toArray(); }要は、ラムダ式内で使いたい変数を外で定義すると、final変数同然になってしまうということ。
そこで、「プリミティブ型の配列という参照型」を使い、中身を変更するという手法をとった。
そうすれば、中身をいくら更新しようとも、参照先を変更していないので怒られない。これならOKstatic double[] vectors_add(double[] vector1, double[] vector2) { final int[] inde = {0}; final int x = 0; return Stream.of(vector1).map(v1 -> v1 + vector2[inde[x]++]).toArray(); }2.文字列関連
2-1.正規表現関連
2-1-1. 正規表現にマッチする「名前」を持つオブジェクトをArrayListから〇〇する
例えば
Person
クラスがメソッドとしてString get_name()
メソッドを持っているとする。
そしてこの戻り値を、Person
型インスタンスの「名前」と呼ぶことにする。2-1-1-1. 取得する
static class Person { private String name; Person(String name){this.name = name;} String get_name(){return this.name;} } static java.util.List<Test.Person> get_person_by_name_regex(java.util.List<Test.Person> list, String regex) { java.util.List<Test.Person> list_tmp = new ArrayList<>(list); //ディープコピー list_tmp.removeIf(person -> !person.get_name().matches(regex)); return list_tmp; } public static void main(String...args) { Person[] sanaesan_members = { new Test.Person("福田 早苗"), //福田家 妻 new Test.Person("福田 松男"), //〃夫 new Test.Person("福田 太郎"), //〃長男 new Test.Person("磯野 網平"), //磯野家 夫 new Test.Person("磯野 久根"), //磯野家 妻 new Test.Person("磯野 和夫"), //磯野家長男 new Test.Person("磯野 若菜"), //磯野家次女 new Test.Person("磯野 玉")//磯野・福田家愛猫 }; java.util.List<Test.Person> list = new ArrayList<>(Arrays.asList(sanaesan_members)); for(Test.Person isono : Test.get_person_by_name_regex(list, "^磯野.*$")) System.out.println(isono.get_name()); //磯野 網平 磯野 久根 磯野 和夫 磯野 若菜 磯野 玉 }2-1-1-2. 消去する
static class Person { private String name; Person(String name){this.name = name;} String get_name(){return this.name;} } static void remove_person_by_name_regex(java.util.List<Test.Person> list, String regex) { list.removeIf(person -> person.get_name().matches(regex)); } public static void main(String...args) { Person[] sanaesan_members = { new Test.Person("福田 早苗"), //福田家 妻 new Test.Person("福田 松男"), //〃夫 new Test.Person("福田 太郎"), //〃長男 new Test.Person("磯野 網平"), //磯野家 夫 new Test.Person("磯野 久根"), //磯野家 妻 new Test.Person("磯野 和夫"), //磯野家長男 new Test.Person("磯野 若菜"), //磯野家次女 new Test.Person("磯野 玉")//磯野・福田家愛猫 }; java.util.List<Test.Person> list = new ArrayList<>(Arrays.asList(sanaesan_members)); Test.remove_person_by_name_regex(list, "^磯野.*$"); for(Test.Person hukuda : list) System.out.println(isono.get_name()); //福田 早苗 福田 松男 福田 太郎 }