20200317のJavaに関する記事は3件です。

初心者から始めるJava、オーバーライド

はじめに

この記事は備忘録である。
参考書レベルの内容だが、本記事に掲載するコードについては、
間違えたものが中心となる。これは実際にコーディング中に間違えた部分を掲載し、自分で反省するために投稿するという目的によるもの
また、後日にJavaSilver試験問題の勉強を兼ねて復習するため、深い部分の話はここでは触れない。

環境

言語:Java11、JDK13.0.2
動作環境:Windows10

オーバーライド

前回でスーパークラスとサブクラスの関係は扱った。

サブクラスにはスーパークラスのメソッドが継承されているが、同じメソッド名を使いながら別の処理を定義したいということがある。
例えばshow〇〇()でメンバをチェックするためのメソッドを用意する場合。同じチェックするにしてもwatch〇〇(),see〇〇(),check〇〇()と使い分けを作ることはできるだろう。だが、引数や型がそれぞれ違うとなれば覚えて使うのが非常に困難になるし、それだけコードが無駄に長くなれば可読性も落ちる。
サブクラスには、スーパークラスで実装されたものと全く同じメソッドを新たに定義することができ、オブジェクトから呼び出す場合サブクラスの方が優先される。

条件としては、
1.シグニチャ(メソッド名、引数リストの型、数、順番)が同じ
2.アクセス修飾子が同じかより緩い
3.戻り値の型が原則同じ(共変戻り値を除く)

サブクラスのメソッドが、スーパークラスのメンバに代わって機能することをオーバーライド(overriding)と呼ぶ。

スーパークラスでまとめて扱う

何度も書くが、サブクラスにはスーパークラスのメンバが継承されている。そしてオーバーライドでサブクラス専用の処理を書いて優先的に機能させることが出来る。
よって、同じスーパークラスの子であるサブクラスを使う際、スーパークラスのメソッドで一括して処理を記述することが出来る。

arrayCatsShow.java
class 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クラスをもとに、HouseCatMyCatのクラスを用意する。

arrayCatsShow.java
class HouseCat extends WildCat
{

  public void show()
  {
    System.out.println("このイエネコは" + number + "番目です。" );
  } 
}

class MyCat extends WildCat
{
  public void show()
  {
    System.out.println("このうちの猫は" + number + "匹目で、今の体重は" + weight + "でした。" );
  }
}

そのうえで、個別の猫を用意する。

arrayCatsShow.java
class 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.java
class HouseCat extends WildCat
{
  public void show()
  {
    super.show();
    System.out.println("このイエネコは" + number + "番目です。" );
  }
}

super.メソッド名でスーパークラスの処理をそのまま持ってこれるので、あらためてすべて書く必要はない。これはフィールドにも言えることなので、super.フィールドでスーパークラスの値を持ってくることもできる。

ところで、スーパークラスのメソッドをオーバーライドされたくないときは、public final void show()のようにfinalをつければオーバーライドできなくなる
これはクラス名とフィールドでもできるので、finalクラスならサブクラスの拡張不可になり、finalフィールドならフィールドの値が宣言時の初期化を除いて変更されない定数(constant)になる。

おわりに

共変戻り値という言葉をSilver問題集から見つけたが、近いうちにクラスライブラリを調べるつもりなので、そこでクラス間の関係をはっきりさせてから扱いたい。

参考

出来るだけ自分で変数や式を書いてコンパイルしているので、完全に引用する場合はその旨記述する。

やさしいJava 第7版
Java SE11 Silver 問題集(通称黒本)

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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:   bionic

Java 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を使用して、ネットワークインターフェースにアクセスします。

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
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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変数同然になってしまうということ。

そこで、「プリミティブ型の配列という参照型」を使い、中身を変更するという手法をとった。
そうすれば、中身をいくら更新しようとも、参照先を変更していないので怒られない。

これならOK
    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();
    }

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()); //福田 早苗  福田 松男  福田 太郎

    }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む