疲れたらやすむ

Javaを学ぶ上でハマったところを書いていきます。iPhoneアプリ開発や日常ネタもあるかも。

【Java】その条件分岐は必要?その変数は必要?

職場でコーディングをしていてふと思いました。

この変数って定義する必要があるのかなあ・・・。
定義しなくても書けるし、かと言って定義した方が何回か使いまわせる。

今回はそんな必要か必要じゃないかの判断に迷う場合について考えてみます。

必要かどうかの判断に迷う状態とは?

まず私が判断に迷う状態。

例えばこんな時です。

ソース

String[] array = new String[] { "A", "B," };

if (array.length > 0) {
	for (String s : array) {
		// 処理
	}
}

for文に入る前のif文ですね。
配列のサイズが0よりも大きければループを行う条件になっています。
一見すると理にかなっているような気もしますが、実際のところこの条件が無くてもちゃんと動きます。


変数の場合はこんな時に迷っちゃいます。

ソース

Student student = new Student("Taro", 1);

String name = student.getName();

if (null != name) {
	String str = name;
}

Studentクラスは内容の内容は省きますが、よくあるBeanのクラスです。
String型のnameとint型のidをプライベートなフィールドに持ち、ゲッターセッターが備わったやつ。
そこから、nameを一度変数に代入し、以後はその変数を条件の判定し使ったり処理に使ったりします。

ゲッター程度なら、毎回ゲッター呼んでもいいような気がしちゃいます。

私個人の考え

極論で言うと、プログラムは動けば良いのです。
プログラムが正常に動いている以上それは正解。

ただし、メンテナンスのコストとか他の人が目にする場合は可読性を少しでも高める必要があります。
基本的に会社で開発を行う場合はほとんど可読性も求められるでしょう。

以上のことから、結局は正解は時と場合によるし、これが正しいという決まりはないと思います。

でも強いて言えば、自分だけが目にするソースだとしても可読性が高いに越したことはありません。
ですので、そう言った意味では常に読みやすいコードを記述したいですよね。

基本的には、ソースは短い方がコンパクトで読みやすいと言えると思います。
しかし無理やりステップ数を減らすよりは、冗長に感じても書いておいた方が良いif文や変数もあるでしょう。
そのあたりは経験とかコーディング規約を神様にして書いていくしかないです。

それを踏まえて、先ほどの判断に迷うソースをもう一度見直して記述してみます。

判断に迷ったらこんな感じで落ち着いた

まずは最初のif文の例。

配列に対しサイズを見てからfor文で回すのは、私は不要だと思います。
サイズが0より大きければ回す場合はですけど。

ソース

String[] array = new String[] { "A", "B," };

for (String s : array) {
	// 処理
}

この状態で意味は伝わるんじゃないかなと思います。
for文の仕組み上、サイズがもし0だったとしても例外は投げませんし、1回もループせずに次の処理に移ります。


そして変数の場合。
これは結構判断が難しいですね。

今回の場合はゲッターで代入して使い回しているので、ぎりぎり不要かなと思います。

ソース

Student student = new Student("Taro", 1);

if (null != student.getName()) {
	String str = student.getName();
}

クラスの変数自体はstudentとして定義していますし、それを使ってゲッターを呼ぶのであれば毎回呼んでも良さそう。
でも、その後も4回、5回と使うなら変数に保持するのもアリかな・・・
2回や3回使う程度ならゲッターで済ましてしまうと思います。

変数に保持しない理由としては、ゲッターはメンバ変数をただ引っ張ってくるだけの処理だからです。
パッと見ても「ああ、これはクラスの中のフィールドを取り出しているだけなんだろうな。」と予測が付きますし、メソッドの処理もメンバ変数のreturnだけ。
それなら何回か使用されていても別に良いんじゃないかなという考えです。

ただし、処理が複雑なメソッドの戻り値の場合は変数に代入しておくことを薦めます。
理由はずばり、その方がわかりやすいと思うからです。

処理が複雑なメソッドを何回も呼び出すと、そのメソッドの処理を追う機会が多くなります。
呼び出す度に処理を追っていては、ソースの全貌を把握するのも時間がかかってしまいます。
多分処理的にも、同じ戻り値を扱うなら変数に格納して使った方が処理速度は早いはず。

迷わないためにもブレないルールを決める

開発には時間が付き物。
設計に時間をかけてしまうと、開発工程の工数が削られてしまう場合もあります。
そんな時にちょっとした迷いで時間を割くのはなるべく避けたいですよね。

なので、自分の中のコーディング規約をある程度決めておくと良いと思います。

その日その日で思いのままにコーディングをしてしまうと、どうしても仕上がったソースに統一感が無かったり。

ちょっと趣旨はずれますが、コメントを記述する「//」のあとに半角スペースを入れたり入れなかったりするのを統一するだけでもかなり綺麗に見えますよ。
ちなみに私は半角スペース入れる派です。

それはそうと、途中にも書きましたがプログラムなんて動いていればOKという考え方もあります。
あまり深くは考えず、自分なりの綺麗を目指しても良いのではないでしょうか。

どんどんコーディングをしていくうちに自分の定型が見つかると思います。
そしていろんな人のソースを見る。これ大事。
そこから得られるものってかなりあります。

お手本をする人を探して真似しちゃうのも全然アリ。
私は結構人のをパクっちゃうタイプなので、職場ではソースを眺めている時間が長かったりします。
そこから自分のルールを作り、この時はこう書く!と決めておくと良いと思います。

住民税の期別に第1期がない?

技術ネタではないですがちょっと気になったこと。

個人で住民税を納付されている方は6月末が第1期の納期です。
いよいよこの時期がやってきましたね。

見たくもない課税課からの封筒が、先日届いてしまいました。
中身は納税通知書と支払い用紙。

しかし、良くも悪くも私の手元に届いた納税通知書には第1期がありませんでした。

届いた納税通知書に第1期の記載がない

f:id:chibiCat:20190626003138j:plain

ご覧の通り、届いた納税通知書には第1期の記載がありません。
そして納税通知書と一緒に、第2期から第4期までの3回分の支払い用紙が同封されていました。

まさか第1期は払わなくても良い?なんて思いが頭をよぎります。

とは言え、仮に第1期に支払なくとも、その分を第2期以降に割り振られるのかな?とも考えました。
そもそもまだ払わなくて良いとは決まってもいない。

気になってしょうがなかったので課税課の方に聞いてみました。

第1期の支払いはどうするのか?

結論から言いますと、第1期の支払いはありません

ただし、です。
本来納税するはずのお金が闇に葬られるわけではありません。
第1期で支払う分は第2期から第4期までにほぼ均等に割り振られており、結果として支払うこととなります。
ですよねーって感じでした。

でもどうしてこんなことになったのでしょうか。

第1期がない理由

その原因は退職時期でした。

住民税を給料から天引きにしている状態で退職してしまうと、残った分を個人で納めなくてはなりません。
その際に、支払う金額の計算が第1期に間に合いそうにない場合は第1期が消えるみたいです。
私の場合は5月末に離職して第1期に間に合いませんでした。

時期が時期なだけに、6月末期日の第1期の分がない!?と少し焦ってしまいました。
6月以降であれば、既に第1期の支払い期日が過ぎているので第2期からなのはわかるのですが。

少しレアなケースだと思いますが、そんな理由でした。

【Java】TreeSetで独自クラスを扱う

今回はTreeSetに的を絞って解説していきます。

Setとは?ListやMapとの違い

Setとはコレクションの1つで、重複した値を持たない特徴があります。

Main.java

import java.util.HashSet;
import java.util.Set;

public class Main {
	public static void main(String[] args) {
		Set<String> set = new HashSet<String>();
		set.add("apple");
		set.add("orange");
		set.add("apple");

		for(String s : set) {
			System.out.println(s);
		}
	}
}

このように同じ値をaddした場合しても

実行結果

orange
apple

重複している値は追加されていません。

Listはaddすれば問答無用で追加するのに対し、Setはaddしても重複する値があれば要素に追加されません。

Mapと比較してみると、重複する値を持たないと言う部分ではキーと似ているかもしれません。
ですが、Mapはキーに対して値を持っているため、どちらかと言えばListの方が似ているかも。

Setの種類について

Setの代表的なものとして、HashSetとTreeSetが存在します。

基本的な理解としては、
「HashSetは格納された要素をソートしないがnullを格納できる」
「TreeSetは格納された要素をソートするがnullは格納できない」
で良いかと思います。

他にもLinkedHashSetと言うものがありますが、全く使ったことがないのでよくわかっていません。
処理速度とかの話なんですかね。

TreeSetの使い方

TreeSetは格納された要素をソートするため、ソート順を決める必要があります。

IntegerやStringなどの基本的な型の場合はそのまま使用可能。
その理由は、IntegerやStringのクラス定義でComparable<T>インターフェースを実装し、compareToメソッドでソート方法を宣言しているからです。

Integerの場合は、数値が小さい順でソートされます。

Main.java

import java.util.Set;
import java.util.TreeSet;

public class Main {
	public static void main(String[] args) {
		Set<Integer> set = new TreeSet<Integer>();
		set.add(3);
		set.add(1);
		set.add(2);

		for (Integer i : set) {
			System.out.println(i);
		}
	}
}

実行結果

1
2
3

Stringの場合はアルファベット順。

Main.java

import java.util.Set;
import java.util.TreeSet;

public class Main {
	public static void main(String[] args) {
		Set<String> set = new TreeSet<String>();
		set.add("B");
		set.add("A");
		set.add("C");

		for (String s : set) {
			System.out.println(s);
		}
	}
}

実行結果

A
B
C

独自クラスでTreeSetを扱う場合

まずソート順を指定しなかった場合どうなるか見てみます。

とりあえず独自クラスを用意します。

Student.java

public class Student {
	int id;
	String name;

	public Student(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public int getId() {
		return this.id;
	}

	public String getName() {
		return this.name;
	}
}

そしてそのままTreeSetに格納してみます。

Main.java

import java.util.Set;
import java.util.TreeSet;

public class Main {
	public static void main(String[] args) {
		Set<Student> set = new TreeSet<Student>();
		set.add(new Student(2, "Jiro"));
		set.add(new Student(1, "Taro"));
		set.add(new Student(3, "Saburo"));

		for (Student s : set) {
			System.out.println(s.getId() + " : " + s.getName());
		}
	}
}

実行結果

Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable

はい。
ClassCastExceptionが発生します。
これは、TreeSetで要素を並び替える際にComparable<T>型にキャストを行う処理があるためです。
つまりキャストを行うには、独自クラスにComparable<T>を実装する必要があります。

Student.java

public class Student implements Comparable<Student> {
	int id;
	String name;

	public Student(int id, String name) {
		this.id = id;
		this.name = name;
	}

	public int getId() {
		return this.id;
	}

	public String getName() {
		return this.name;
	}

	@Override
	public int compareTo(Student o) {
		return this.id - o.id;
	}
}

これで実行してみます。
実行結果

1 : Taro
2 : Jiro
3 : Saburo

ちゃんとソートされていますね。

ちなみに、独自クラス側でComparable<T>を実装しないパターンもあります。
その場合は、使用する側でソート順を指定します。

Set<Student> set = new TreeSet<Student>(Comparator.comparing(Student::getId));

これでStudentにComparable<T>が実装されていなくても、idの昇順で問題なく動作します。
関数型インターフェースやメソッド参照が出てきて少しややこしい部分はありますが。

このあたりはJava Goldでも出題される範囲なのでぜひ押さえておきたいところです。

【Xcode】Failed to create provisioning profileの解決方法

今回はXcodeのエラーの原因と解決方法についてです。

事象

実際のエラーの状態はこんな感じです。

f:id:chibiCat:20190616175302p:plain

Failed to create provisioning profile

No profiles for 'Bundle Identifier' were found

の2つのエラーが表示されます。

このエラーが出ていると、多分ですが実機テストが出来ません。
問答無用でBuild Failedとなってしまいます。
シミュレータではビルド可能です。

原因

私の場合は、問題なく動いていたプロジェクトを削除して、再度同じ名前のプロジェクトを作成した場合にエラーが出ました。

どうやらBundle Identifierが重複しているために怒られているそうです。
物理的にプロジェクトを削除しただけではダメらしい。

そもそもBundle Identifierとは、アプリを区別出来る一意の値です。
そのため、AppStoreで公開する時に被るのは当然、ローカルでも被ってしまうことは許されないのです。

この辺りの仕様が正直よくわかりません。
一意のIDが重複してはいけないのは理解出来るのですが、そもそもプロジェクトを削除しているので平気のはず!と思ってしまいます。

解決方法

簡単に解決するなら、別名のプロジェクトを作成するのが早そうです。

結局のところ調べてもわかりませんでした。
作成したプロジェクトを消すことは想定されていないのかもしれません。

一応Derived Dataのプロジェクト削除も行いましたが効果なし。
方法は、「Xcode」→「Preferences...」の「Locations」タブを開き、Derived Dataの項目のパス内にあるプロジェクトのデータを削除。

f:id:chibiCat:20190616234711p:plain

パスの右側の矢印マークをクリックするとFinderで開いてくれると書いてありましたが私は開きませんでした。
なので、Finder上でCommand+Shift+Gでパスを貼り付けエンターで開きました。

フォルダの中身は自分で作ったプロジェクト名から始まるフォルダと謎のフォルダが2つくらい。
自作プロジェクト名が頭に付いたフォルダだけ削除してXcodeを再起動させましたが変化なしでした。


にしても困る事象ですよね。
プロジェクト削除してるのに重複扱いとは。

何かわかり次第更新します。


【2019年6月22日追記】
後日、再度Xcodeを立ち上げてみると何故かエラーが消えていました。
何ごともなくビルドも通り実機テストも出来ました。

なんでしょう。
やったことは
・キーチェーンの整理(期限切れのものを削除)
・Derived Dataの削除
・実機のiPhone側のデベロッパAppの登録解除
ぐらいですかね。
意外とOSの再起動とかが必要だった可能性もありますが。

ちなみに、再現しようとしてプロジェクトを削除後に同名のプロジェクトを作成してもエラーは出ませんでした。
なぞい。

【Java】ジェネリクスについて

今回はジェネリクスに関する記事になります。

クラスやメソッド宣言に、<T>などと記述されていることがあります。
それがジェネリクスと呼ばれるものです。

ジェネリクスとは

Javaにおいて、ジェネリクスとは「型の安全性を高め、さらに汎用性を持たせる」ことが出来る仕組みです。
普段よく使用するものでは、ListやMapはジェネリクスを使用しています。

自作のジェネリクスクラスは以下の様に定義します。

GenericsSample.java

public class GenericsSample<T> {

	private T t;

	public GenericsSample(T t) {
		this.t = t;
	}

	public T getT() {
		return this.t;
	}
}

これがジェネリクスを使用したクラスで、総称型と呼ばれるクラスになります。
ジェネリクスはクラスやインターフェース、メソッドなどに記述出来ます。

クラス名の右側に<T>と記述されていますが、これを型引数と言います。
<>内の文字は何でもOKですが基本的には以下の通り。

変数名 指針
E コレクションに格納される要素
K, V キーと値
T 上記以外の一般的な要素
R 戻り値

このGenericsSampleを実際に使用する場合はこんな感じになります。

Main.java

public class Main {
	public static void main(String[] args) {

		GenericsSample<String> generics = new GenericsSample<String>("Hello");
		System.out.println(generics.getT());
	}
}

実行結果

Hello

使用には総称型の型引数<T>の部分をパラメータ化する必要があり、今回はString型でパラメータ化しています。
パラメータ化は、後に触れる境界を指定していない場合はどんな型でもOKです。
StringやInteger、独自クラスでもなんでも。
ListやMapなどでも型を指定すると思いますが、それと同じです。

今回のGenericsSampleのソースでは、T型のフィールドtをprivateで宣言しています。
この時点ではTが何型であるのか決まっていません。
ただし実際にはパラメータ化(型の指定)をする必要があるため、実行後は型が決まります。


ジェネリクスをメソッドで使用する場合は以下の様に記述します。

public <T> void doSomething() {
	T t;
}

安全性と汎用性

ジェネリクスを使用することで、予想外の例外を未然に防げたりクラスやメソッドなどの汎用性を高めることが可能です。

まずは安全性について。

少し極端な例ですが、下記のコードがあったとします。

List list = new ArrayList<>();
list.add("Hello");
list.add(new Integer(1));

for (Object o : list) {
	String s = (String) o;
}

Listは総称型であるため、基本的に使用にはそのListは何の型を扱うのかをパラメータ化することが推奨されています。
普段であれば、Listを定義する際にそのListが何型なのかを<>の中に記述するはずです。
しかし記述しない場合でもコンパイルエラーとはならず実行出来てしまいます。

その結果、意図せず複数の型の値が格納されたListが完成してしまいました。

ではfor文で取り出してみましょう。
String型の値しか入っていないと思っていたListの中にInteger型の値の混ざっていました。
その結果、実行時にClassCastExceptionが発生。

つまり、パラメータ化しないと実行するまでバグであることがわからないのです。


そしてパラメータ化した場合。

List<String> list = new ArrayList<String>();
list.add("Hello");
list.add(new Integer(1));

Integer型の値をaddした時点でコンパイルエラーとなります。
バグを未然に防いでくれ、これが型の安全性を高めます。


そして汎用性とは、例えばListでは複数の型でパラメータ化することにより様々な型のListが定義出来ます。

List<String> stringList = new ArrayList<String>();
List<Integer> integerList = new ArrayList<Integer>();

ジェネリクスを使用しない場合、String型やInteger型用のListを定義する必要があり、非常に冗長ですよね。
あまり気にしないで普段使用していますが、意外と身近にジェネリクスの便利な部分が転がっています。

制約がある

ジェネリクスを使用したクラスにはいくつか制約があります。

予めお伝えしておくと、以下のコードは各箇所でコンパイルエラーとなります。

GenericsSample.java

public class GenericsSample<T> {

	static T t1; // 1.

	T t2 = new T(); // 2.

	T[] tArray = new T[1]; // 3.

	public void doSomething() {
		Class<T> cls = T.class; // 4

		T t3;
		boolean b = t3 instanceof T); // 5
	}
}

1.static修飾子の型変数は定義出来ない

2.型変数のインスタンス生成は出来ない

3.型変数が要素の配列は生成出来ない

4.型変数のclass参照は出来ない

5.instanceofによる型の判定は出来ない

型境界

ジェネリクスには型の境界を設けることが可能です。
型境界を指定すると、パラメータ化する型を限定することが出来ます。
たぶん初見でジェネリクスに苦手意識を持ってしまうのはこのあたりのせいだと思います。

この内容では継承が絡んできます。
まずは例として継承関係にあるクラスを作成します。

A.java

public class A {

}

B.java

public class B extends A {

}

C.java

public class C extends B {

}

これらのクラスの関係としては、Aが親でBが子、さらにBが親のCが子となります。

この継承関係で、型境界をBに指定してみます。
型境界は、<>内の型名の後ろにextendsを付けて境界にする型を記述します。

GenericsSample.java

public class GenericsSample<T extends B> {
	
}

このジェネリクスのクラスを実際に使用してみます。

Main.java

public class Main {
	public static void main(String[] args) {

		// コンパイルエラー
		GenericsSample<A> genericsA = new GenericsSample<A>();

		// OK
		GenericsSample<B> genericsB = new GenericsSample<B>();

		// OK
		GenericsSample<C> genericsC = new GenericsSample<C>();

		// コンパイルエラー
		GenericsSample<String> genericsString = new GenericsSample<String>();
	}
}

今回は境界にBを指定しているため、Bとして扱える型であれば許容されます。
つまり、境界に指定した型とis-a関係であれば良いということになります。

AはBを子に持つがBではないためコンパイルエラー。
BはBであるためOK。
Cは親にBを持ち、Bとして扱うことが可能であるためOK。
StringはBとは無関係のためコンパイルエラー。

ワイルドカードと上下限の境界

ジェネリクスをパラメータ化する際に、ワイルドカード(<?>)を使用することが出来ます。
ワイルドカードとは、何らかの型が指定されることを意味しており、つまりどんな型でも許容します。

GenericsSample.java

public class GenericsSample<T> {

}

Main.java

public class Main {
	public static void main(String[] args) {

		// 変数宣言でのワイルドカード(下限境界)
		GenericsSample<? super B> generics = new GenericsSample<>();
	}

	// メソッドの引数でのワイルドカード
	public void doSomething(GenericsSample<?> generics) {

	}
}

この様に、ジェネリクスクラスの変数宣言にワイルドカードを使用します。

<?>を使用した場合は型が決まっておらず、言わばObject型のような感覚です。
実際、<?>は<? extends Object>を省略したものとされています。

そして、型境界でも出てきたextendsと、もう1つsuperを使用し境界を指定出来ます。
superはextendsの逆という解釈でOKです。

先ほどの型境界で登場したA、B、Cそれぞれのクラスが存在した場合。

Main.java

public class Main {
	public static void main(String[] args) {

		// OK
		doSomething(new GenericsSample<A>());

		// OK
		doSomething(new GenericsSample<B>());

		// コンパイルエラー
		doSomething(new GenericsSample<C>());
	}

	static public void doSomething(GenericsSample<? super B> generics) {

	}
}

Bか、Bのスーパータイプを許容するのが<? super B>です。

ワイルドカードやsuperは、型引数には使用出来ないので混同しない様に注意が必要です。