次のコードを確認してください。
interface ParentIF {
void
parentMetnod();
}
@FunctionalInterface
interface FuncIF {
// line 1
public default
void method() { } // line 2
// line 3
}
FuncIFインタフェースを正常にコンパイルするために必要な修正はどれですか?
1.
line 1を以下のように修正する。
interface
FuncIF extends ParentIF {
2.
line 2をコメントアウトする。
3.
line 3に以下のコードを挿入する。
public
static void classMethod() { }
4.
line 3に以下のコードを挿入する。
public
boolean equals(Object obj);
解答: 1
解説:
JavaSE8から導入された関数型インタフェースのルールに関する問題です。
@FunctionalInterfaceアノテーションは、そのインタフェースが関数型インタフェースの要件を満たしているかをコンパイル時に検出するためのアノテーションです。
関数型インタフェースの要件は、以下のとおりです。
・抽象メソッドがひとつのみ定義されていること
・staticメソッド、defaultメソッドは、抽象メソッドとしてカウントされない(つまり、あってもなくても構わない)
・Objectクラスに定義されているメソッドについても、抽象メソッドとしてカウントされない(どのクラスにも間接的に必ず実装されているため)
上記の要件を満たすためには、ParentIFインタフェースにある抽象メソッドparentMetnodをインタフェースFuncIFに継承する必要があるため、選択肢1が正解となります。
次のコードを確認してください。
interface Decorator {
void
decorate(String msg, String symbol);
}
interface Improver {
int improve(int
value);
}
public class Test {
public static
void main(String[] args) {
Decorator
dec = (m, s) -> System.out.println(s + m + s); // line 1
dec.decorate("Hello",
" +++ ");
Improver
imp = v -> v * 2; // line 2
System.out.println(imp.improve(5));
}
}
このコードをリファクタリングして正常にコンパイル、実行できるものはどれですか。(3つ選択してください)
1.
line 1を以下に置き換える。
Decorator
dec = m, s -> System.out.println(s + m + s);
2.
line 1を以下に置き換える。
Decorator
dec = (String m, String s) -> System.out.println(s + m + s);
3.
line 1を以下に置き換える。
Decorator
dec = (m, s) -> { System.out.println(s + m + s); };
4.
line 2を以下に置き換える。
Improver
imp = (int v) -> { v * 2; };
5.
line 2を以下に置き換える。
Improver
imp = v -> return v * 2;
6.
line 2を以下に置き換える。
Improver
imp = (v) -> { return v * 2; };
解答: 2,3,6
解説:
ラムダ式の省略記法に関する問題です。
ラムダ式のアロー演算子( -> )の左辺に引数リスト、右辺に処理の内容を記述しますが、左辺と右辺はそれぞれ独立して省略記法で記述することができます。
左辺については、以下のとおりです。
・引数が1つのみの場合は、()カッコと型を省略できる
(String s) 省略 ⇒ s
・引数が2つ以上のときは、型のみ省略できる
(int i, String s) 省略 ⇒ (i, s)
右辺については、以下のとおりです。
・処理が一文のみの場合は、{}カッコ、returnおよびセミコロンを省略できる
{ return s.length > 3; } 省略 ⇒ s.length > 3
なお、省略記法を用いる場合は、完全に省略記法で書かなければならないた、例えば右辺で{}カッコだけ省略してreturnだけ省略しないといった書き方は認められません。
選択肢1は、左辺に()カッコはがないので誤りです。
選択肢4は、右辺でreturnのみ省略して{}カッコとセミコロンを省略していないので誤りです。
選択肢5は、右辺で{}カッコのみ省略してreturnとセミコロンを省略していないので誤りです。
次のコードを確認してください。
List<Integer> numList = new ArrayList<>();
numList.add(7);
numList.add(29);
numList.add(24);
// line 1
line 1に挿入して正常にコンパイル、実行できるものはどれですか。
1.
Stream<Integer>
strm = numList.stream();
strm.flatMap(n
-> n.stream()).forEach(System.out::println);
2.
Stream<Integer>
strm = numList.stream();
strm.filter(n
-> n > 10).forEach(n -> System.out::println(n));
3.
Stream<List<Integer>>
strm = Stream.of(numList);
strm.map(n
-> n * 10).forEach(System.out::println);
4.
Stream<List<Integer>>
strm = Stream.of(numList);
strm.flatMap(list
-> list.stream()).forEach(System.out::println);
解答: 4
解説:
ストリームの生成方法と要素の操作に関する問題です。
選択肢1は、numListをソースとしてIntegerオブジェクトを要素に持つストリームを生成しています。
その後、flatMapメソッドを呼び出していますが、Integerオブジェクトである要素 n に対してstreamメソッドを呼び出しているのでコンパイルエラーとなります。
選択肢2は、選択肢1と同様にnumListをソースとしてIntegerオブジェクトを要素に持つストリームを生成しています。
その後、filterメソッドで10より大きい要素のみ通過させ、最後にforEachメソッドで出力しようとしています。
しかし、forEachメソッドの引数としてラムダ式とメソッド参照が混在したような書き方をしており、正確な仕様に則った書き方ではないためコンパイルエラーとなります。
選択肢3は、Streamインタフェースのofメソッドを使用して、IntegerのListひとつを要素として持つストリームを生成しています。
その後、mapメソッドで要素を変換しようとしているのですが、Listオブジェクトに対して n * 10 のような演算はできないため、コンパイルエラーとなります。
選択肢4は、選択肢3と同様にStreamインタフェースのofメソッドを使用して、IntegerのListひとつを要素として持つストリームを生成しています。
その後、flatMapメソッドで要素を変換しています。
flatMapメソッドは、ストリームの要素であるListオブジェクトをソースとしてさらにストリームを生成し、これを元のストリームの要素と置き換える働きをします。
つまり、flatMapメソッド後の状態は、Integerオブジェクトを要素として持つストリームになります。
これに対してforEachメソッドを呼び出し、すべての要素を出力しています。
問題のない処理は選択肢4ということになります。
次のコードを確認してください。
Set<String> strSet = new TreeSet<>();
strSet.add("8"); strSet.add("6");
strSet.add("3");
// line 1
strSet.stream().mapToInt(func).count();
line 1に挿入して正常にコンパイル、実行できるものはどれですか。
1.
ToIntFunction<String>
func = s -> Integer.parseInt(s);
2.
Function<String,
Integer> func = s -> Integer.parseInt(s);
3.
BiFunction<String,
String, Integer> func = s1, s2 -> Integer.parseInt(s1);
4.
IntConsumer func =
s -> Integer.parseInt(s);
解答: 1
解説:
java.util.functionパッケージの関数型インタフェースに関する問題です。
選択肢2のFunctionは、ひとつの引数をもとに結果を返す処理を表します。
選択肢3のBiFunctionは、引数を2つ渡すタイプのFunctionです。
選択肢1のToIntFunctionは、引数をひとつ受け取ってプリミティブ型のint値を結果として返すFunctionです。
選択肢4のIntConsumerは、引数としてプリミティブ型のint値を受け取り、処理をして結果を返さないものです。
ここでは、ストリームのmapToIntメソッドを呼び出しており、渡せる処理としてはToIntFunction型なので選択肢1が正解となります。
ちなみに、mapToIntメソッドは、ストリームの要素をプリミティブ型のint値に変換することで、元となるストリームをIntStream型に変換するものです。
次のコードを確認してください。
class Employee {
String name;
int age;
public
Employee(String name, int age) {
this.name =
name;
this.age =
age;
}
public String
init() { return name.substring(0, 1); }
public int
getAge() { return age; }
}
public class Test {
public static
void main(String[] args) {
Employee
emp1 = new Employee("Vicente", 38);
Employee
emp2 = new Employee("Laura", 31);
BiPredicate<Employee, Employee> bPre =
(e1, e2) -> e1.getAge() == e2.getAge(); // line 1
System.out.println(bPre.test(emp1, emp2)); // line 2
BiFunction<Employee, Employee, String> bFunc =
(e1, e2) -> e1.init().concat(e2.init()); // line 3
System.out.println(bFunc.apply(emp1, emp2)); // line 4
}
}
このコードをコンパイルおよび実行すると、どのような結果になりますか。
1.
正常に実行され、以下のように出力される。
false
VL
2.
line 1でコンパイルエラーが発生する。
3.
line 2でコンパイルエラーが発生する。
4.
line 3でコンパイルエラーが発生する。
5.
line 4でコンパイルエラーが発生する。
6.
実行時に例外がスローされる。
解答: 1
解説:
java.util.functionパッケージの関数型インタフェースに関する問題です。
BiPredicateは、2つの引数を取り、処理をした結果としてtrue/falseのboolean値を返す処理を表します。
BiFunctionは、2つの引数をもとに結果を返す処理を表します。
line 1では、Employeeオブジェクト2つのageを比較して、同じであればtrueを返すような処理としてBiPredicateオブジェクトを生成しています。
line 2で、2つのEmployeeオブジェクトを引数として渡し、BiPredicateインタフェースのメソッドであるtestを実行しているため、falseとなります。(38と31を比較しているため)
line 3では、Employeeオブジェクト2つのinitメソッドの戻り値をconcatメソッドで連結するような処理としてBiFunctionオブジェクトを生成しています。
line 4で、2つのEmployeeオブジェクトを引数として渡し、BiFunctionインタフェースのメソッドであるapplyを実行しているため、VLとなります。("Vicente"と"Laura"の1文字目を連結しているため)
よって、選択肢1のような結果になります。
次のコードを確認してください。
String[] arrStr = { "Madrid", "Barcelona"
};
Stream<String> strm = Arrays.stream(arrStr);
strm.map(s -> s.toUpperCase())
.forEach(System.out::println);
strm.filter(s -> s.length() >= 7)
.forEach(System.out::println);
このコードをコンパイルおよび実行すると、どのような結果になりますか。
1.
正常にコンパイルおよび実行でき、以下のように表示される。
MADRID
BARCELONA
BARCELONA
2.
正常にコンパイルおよび実行でき、以下のように表示される。
MADRID
BARCELONA
Barcelona
3.
コンパイル・エラーが発生する。
4.
実行時に例外がスローされる。
解答: 4
解説:
ストリーム・オブジェクトの性質に関する問題です。
ストリームは、ソースから生成したのちに、中間操作を経て終端操作によって「消費」されます。
いったん消費されたストリームを再度使用するには、同じソースから新しいストリームを別途生成する必要があります。
この設問では、Arraysクラスのstreamメソッドを使用して生成したストリームstrmをforEachメソッド(終端操作)で2度消費使用としているので、実行時にエラーになります。
したがって、選択肢4が正解です。
なお、一度目の終端操作のあとに
strm = Arrays.stream(arrStr);
として、再度ストリームを生成すれば、結果は選択肢2のような出力になります。
次のコードを確認してください。
class Bag {
String color;
public
Bag(String color) { this.color = color; }
public String
getColor() { return color; }
}
public class Test {
public static
void main(String[] args) {
List<Bag> bList = new ArrayList<>();
bList.add(new Bag("Black"));
bList.add(new Bag("Navy"));
bList.add(new Bag("Red"));
bList.add(new Bag("Beige"));
Stream<Bag> bs = bList.stream();
// line 1
}
}
このコードを正常にコンパイルし、以下のように出力するためには、どのコードをline 1に挿入しますか。
Black
Navy
Beige
1.
bs.map(Bag::getColor)
.filter(b
-> b.getColor().length() > 3)
.forEach(s
-> System.out.println(s));
2.
bs.filter(b ->
b.getColor().length() > 3)
.map(Bag::getColor)
.forEach(s
-> System.out.println(s));
3.
bs.map(b ->
b.getColor().length() > 3 ? true : false)
.filter(b
-> b.toString().equals("true"))
.forEach(s
-> System.out.println(s));
4.
bs.filter(Bag b
-> b.getColor().length() > 3)
.map(Bag
b -> b.getColor())
.forEach(System.out::println);
解答: 2
解説:
ストリームの操作と要素に関する総合的な問題です。
選択肢1では、mapメソッドにBagクラスのgetColorメソッドをメソッド参照のかたちで渡しています。
これによって、ストリームの要素のBagオブジェクトが、そのcolorのStringオブジェクトに変換されます。
次のfilterメソッドで、要素のStringオブジェクトに対してgetColorメソッドを呼んでいるのでコンパイルエラーとなります。
選択肢2では、filterメソッドでcolorの長さが3を超えるBagオブジェクトだけを通過させています。
その後、BagクラスのgetColorメソッドを使用して、ストリームの要素をBagオブジェクトからStringオブジェクトに変換しています。
このStringオブジェクトの要素は、Bagオブジェクトのcolorなので、設問のような出力になり、この選択肢が正解となります。
選択肢3では、mapメソッドでBagオブジェクトのcolorの長さが3を超えるどうかのtrue/falseに変換しています。
三項演算子を使用しているのでややトリッキーですが、ここで変換されたものはbooleanのラッパークラスであるBooleanオブジェクトとして変換されています。
次のfilterメソッドで、BooleanオブジェクトのtoStringメソッドの戻り値が"true"になるものだけ通過させて、最後に出力しています。
処理自体は問題なく実行できるのですが、出力されるものが"true"3つになってしまうため、設問の趣旨を満たしません。
選択肢4では、ラムダ式の省略記法として左辺が不正確な書き方です。
()カッコを省略したのであれば、型も省略する必要があるので、(Bag b)とするか、単に b としなければコンパイルエラーになります。
次のコードを確認してください。
Stream<String> source =
Stream.of("1.4", "5.0", "6.0",
"7.0", "8.0");
DoubleStream dblStrm =
source.mapToDouble(Double::parseDouble);
DoubleStream filtered =
dblStrm.filter(d -> d >= 7.0)
.peek(System.out::println);
Stream<String> converted =
filtered.mapToObj(String::valueOf);
converted.peek(System.out::println);
このコードをコンパイルおよび実行すると、どのような結果になりますか。
1.
以下のように出力される。
7.0
7.0
8.0
8.0
2.
以下のように出力される。
7.0
8.0
7.0
8.0
3.
何も出力されない。
4.
コンパイル・エラーが発生する。
5.
実行時に例外がスローされる。
解答: 3
解説:
ストリームの中間操作と終端操作に関する問題です。
設問では、最初にStreamインタフェースのofメソッドで"1.4"から"8.0"までの文字列を要素に持つストリームを生成しています。
次に、要素の文字列をプリミティブのdouble型として変換するmapToDoubleメソッドを呼び出しています。
その後、filterメソッドで7.0以上の値のみ通過させ、peekメソッドでいったん出力します。
これを再びString型に変換するmapToObjメソッドを呼び出し、最後に要素を出力するpeekメソッドを呼び出しています。
ここまでは、すべて中間操作となり、このストリームに対して終端操作を呼んでいないことになります。
ストリームは、終端操作を実行することで要素が消費されていきます。
そのため、このままでは何も実行されず、何も出力されないという選択肢3が正解になります。
ちなみに、最後のconverted.peekをconverted.forEachに変更すると、
7.0
7.0
8.0
8.0
と出力されます。
要素7.0のpeekとforEach、8.0のpeekとforEachという順番で処理されていくためです。
次のコードを確認してください。
class City {
String name;
double
population;
public
City(String name, double population) {
this.name =
name;
this.population = population;
}
public double
getPopulation() { return population; }
}
public class Test {
public static
void main(String[] args) {
Stream<City> cStream =
Stream.of(
new City("Chicago", 2.695),
new City("Yokohama", 3.689),
new City("Madrid", 3.165));
boolean
result =
// line1
System.out.println(result);
}
}
このコードを正常にコンパイルし、trueと出力するためには、どのコードをline 1に挿入しますか。
1.
cStream.allMatch(c
-> c.getPopulation() > 3.0)
.allMatch(c
-> c.getPopulation() > 3.2)
.allMatch(c
-> c.getPopulation() > 2.7);
2.
cStream.findAny(c
-> c.getPopulation() > 2.8)
.get()
.getPopulation()
> 3.1;
3.
cStream.mapToInt(c
-> c.getPopulation())
.sum()
< 10;
4.
cStream.map(c
-> c.getPopulation())
.filter(c
-> c > 3)
.count()
> 1;
解答: 4
解説:
短絡終端操作とラッパークラス・オブジェクトの扱いに関する問題です。
選択肢1は、allMatchメソッドを使用していますが、戻り値がbooleanになるので、連鎖的に呼ぶことはできずコンパイルエラーになります。
選択肢2は、findAnyメソッドを使用していますが、このメソッドはストリームの任意の要素をOptinal型としてひとつ返すもので、引数は取れません。
そのため、コンパイルエラーとなります。
選択肢3は、mapToIntメソッドでストリームの要素をプリミティブ型のint値に変換し、IntStreamにしようとしています。
しかし、CityオブジェクトのgetPopulationメソッドの戻り値がdouble型であるため、int型に変換できずにコンパイルエラーとなります。
ラムダ式の右辺を(int)c.getPopulation()とすれば、コンパイルおよび実行ともに正常に終了します。
選択肢4は、mapメソッドでストリームの要素であるCityオブジェクトをdouble値に変換しています。
このとき、mapToDoubleメソッドではなくmapメソッドを使用しているので、ストリームはDoubleStream型ではなくStream<Double>型になります。
次にfilterメソッドで3を超えるもののみ通過させていますが、ここではストリームの要素がDoubleからdoubleにアンボクシングされて比較されます。
最後に、通過した要素の数が1より大きいかどうかを > 演算子で比較し、boolean値が変数resultに格納されます。
よって、選択肢4が正解となります。
次のコードを確認してください。
Optional<String> str = Optional.of(null); // line 1
System.out.println(str.orElse(null)); // line 2
System.out.println(str.get()); // line 3
System.out.println(str.isPresent()); // line 4
str.ifPresent(System.out::println); // line 5
このコードをコンパイルして実行したとき、どの行で実行時エラーが発生しますか。(最初に発生する行を選択してください)
1.
line 1
2.
line 2
3.
line 3
4.
line 4
5.
line 5
解答: 1
解説:
JavaSE8で導入されたOptionalクラスに関する問題です。
Optionalオブジェクトは、staticメソッドのofメソッドもしくはofNullableメソッドを使用して生成します。(Optional.empty()で、空のOptionalインスタンスを生成することもできます)
line 1では、ofメソッドを使用してOptionalオブジェクトを生成していますが、このメソッドにはnullを引数として渡すことはできない(NullPointerException発生)ため、ここで実行時例外が発生します。
ちなみに、line 1をofNullableと修正すると、次はline 3でNullPointerExceptionが発生するようになります。
それ以外は、実行時例外が発生する可能性がある部分はありません。
次のコードを確認してください。
class City {
String name;
int ward;
public
City(String name, int ward) {
this.name =
name;
this.ward =
ward;
}
public String
getName() { return name; }
public int
getWard() { return ward; }
public String
toString() { return name + ":" + ward; }
}
public class Test {
public static
void main(String[] args) {
List<City> cities = Arrays.asList(
new
City("Yokohama", 18),
new
City("Hamamatsu", 7),
new City("Nagoya", 16),
new
City("Fukuoka", 7)
);
cities.stream().sorted(
Comparator.comparing(City::getWard)
.thenComparing(City::getName)
.reversed()
).forEach(System.out::println);
}
}
このコードをコンパイル、実行すると何が出力されますか。
1.
コンパイルエラーが発生する。
2.
Fukuoka:7
Hamamatsu:7
Nagoya:16
Yokohama:18
3.
Yokohama:18
Nagoya:16
Fukuoka:7
Hamamatsu:7
4.
Yokohama:18
Nagoya:16
Hamamatsu:7
Fukuoka:7
解答: 4
解説:
Comparatorインタフェースに関する問題です。
ストリームのSortedメソッドは、引数として指定されたComparatorオブジェクトのロジックに基づき、ストリーム内の要素を並び替えます。
また、Comparatorインタフェースのstaticメソッドを使用して、Comparatorオブジェクトを生成することができます。
comparingメソッドは、引数としてFunction型を取り、そのFunction型操作の戻り値となる値をもとに自然順序付けで並び替えを行うComparatorオブジェクトを返します。
設問では、返ってきたComparatorオブジェクトに対して、さらに連鎖的にthenComparingやreversedを呼んで追加の並べ替えロジックを適用したComparatorオブジェクトを生成しています。
まとめると、以下のようになります。
comparing(City::getWard) … Cityオブジェクトのwardの数で並べ替え
thenComparing(City::getName) … wardの数で並べ替えたものについて、同じwardのものはさらにnameでアルファベット順に並べ替える
reversed() … 全体の並べ替えロジックを逆順にする
よって、ward数の7, 16, 18で並べ替えられたもののうち、同じward数のHamamatsuとFukuokaが名前順で並べ替えられ、最後に全体が逆順となるような並べ替えになります。
次のコードを確認してください。
class Country {
private String
name;
private String
lang;
public
Country(String name, String lang) {
this.name =
name;
this.lang =
lang;
}
public String
getLang() { return lang; }
public String
toString() { return name; }
}
public class Test {
public static
void main(String[] args) {
List<Country> countries = new ArrayList<>();
countries.add(new Country("Spain", "es"));
countries.add(new Country("USA", "en"));
countries.add(new Country("Argentine", "es"));
countries.add(new
Country("Australia", "en"));
Predicate<Country> p = c -> c.getLang().equals("es");
countries.stream()
//
line1
.forEach((k, v) -> System.out.println(v));
}
}
このコードを正常にコンパイルし、以下のように出力するためには、どのコードをline 1に挿入しますか。
[USA, Australia]
[Spain, Argentine]
1.
.collect(Collectors.groupingBy(p))
2.
.collect(Collectors.joining(p))
3.
.collect(Collectors.partitioningBy(p))
4.
.collect(Collectors.toMap(p))
解答: 3
解説:
終端操作の一種であるリダクション操作に関する問題です。
選択肢1は、CollectorsクラスのgroupingByメソッドを使用して、ストリームの要素をMapとして集約する処理です。
ここでは、ストリームの要素からMapオブジェクトのKeyを抽出する処理を渡す必要があるため、引数としてはPredicateではなくFunctionオブジェクトを指定する必要があります。
よって、これはコンパイルエラーとなります。
選択肢2は、Collectorsクラスのjoiningメソッドを使用していますが、このメソッドはストリームの要素の文字列を連結するために使用するもので、使い方自体が誤りでコンパイルも成功しません。
選択肢3が正解ですが、ここではCollectorsクラスのpartitiningByメソッドを使用しています。
partitiningByメソッドは、引数として渡したPredicateオブジェクトの処理結果をもとに、ストリームの要素をtrueとfalseをキーとするMapに格納します。
選択肢4は、CollectorsクラスのtoMapメソッドを使用して、ストリームの要素をMapオブジェクトに格納しようとしています。
このtoMapメソッドには、MapのKey抽出FunctionオブジェクトとValue抽出Functionオブジェクトの、少なくとも2つのFunctionオブジェクトを引数として渡す必要があるため、コンパイルエラーとなります。
次のコードを確認してください。
class Student {
private String
name;
private int
score;
public
Student(String name, int score) {
this.name =
name;
this.score
= score;
}
public int
getScore() { return score; }
public String
toString() { return name; }
}
public class Test {
public static
void main(String[] args) {
List<Student> sList = Arrays.asList(
new
Student("Sergio", 78),
new
Student("Maria", 83),
new
Student("Daniel", 95),
new
Student("Ana", 78)
);
Stream<Student> stm1 =
sList.stream(); // line 1
Stream<Student> stm2 =
sList.stream(); // line 2
stm1.mapToInt(s -> s.getScore())
.filter(n -> n > 70)
.average()
.ifPresent(System.out::println);
stm2.distinct()
.map(Student::getScore)
.findAny()
.ifPresent(System.out::println);
}
}
このコードを一部変更したときの記述として、正しいものはどれですか。
1.
line 1をsList.parallelStream()としても実行結果に影響はない。
2.
line 2をsList.parallelStream()としても実行結果に影響はない。
3.
line 1をsList.parallelStream()とするとコンパイルに失敗する。
4.
line 2をsList.parallelStream()とするとコンパイルに失敗する。
解答: 1
解説:
並列ストリームに関する問題です。
設問の処理を詳しく見ていくと、まずstm1はmapToIntメソッドでストリームの要素をStudentオブジェクトからint型(score)に変換しています。
その後、filterメソッドで70を超える要素のみを通過させ、averageメソッドで平均値を計算しています。
averageメソッドの戻り値がOptionalDouble型なので、ifPresentメソッドを使用して、要素がnullでない場合のみ出力するようにしています。
また、stm2はdistinctメソッドで重複するオブジェクトを排除し、mapメソッドでストリームの要素をStudentオブジェクトからint型(score)に変換しています。
その後、任意の要素をOptinal型としてひとつ取得し、ifPresentメソッドを使用して、要素がnullでない場合のみ出力するようにしています。
stm1の方は、平均値を出す処理なので、ストリームの要素の処理順について制約はありません(並列でも直列でも処理結果は変わらない)。
それに対してstm2の方は、ストリームの要素の処理順が変わると、出力結果も変わってくる可能性があります。
よって、選択肢1が正解となります。
次のコードを確認してください。
Stream<String> ss = Stream.of("Oracle",
"Java", "Duke");
BinaryOperator<String> bo = (s1, s2) ->
s1.concat(s2); // line 1
String label = ss.reduce(bo); // line 2
System.out.println(label);
このコードを正常にコンパイルおよび実行するためには、どのように修正しますか。(2つ選択してください)(修正は、ひとつのみで要件を満たすものとします)
1.
line 1を以下のように修正する。
BiFunction<String,
String, String> bo = (s1, s2) -> s1.concat(s2);
2.
line 1を以下のように修正する。
UnaryOperator<String>
bo = (s) -> s.toUpperCase();
3.
line 2を以下のように修正する。
String
label = ss.reduce(String::new, bo);
4.
line 2を以下のように修正する。
String
label = ss.reduce("", bo);
5.
line 2を以下のように修正する。
String
label = ss.reduce(bo).get();
解答: 4,5
解説:
【解答】
終端操作の一種であるリダクション操作に関する問題です。
Streamインタフェースのreduceメソッドは、BinaryOperatorオブジェクトを使用してストリームの要素をひとつにまとめる処理をします。
このとき、引数としてBinaryOperatorのみを渡すと、結果はOptional型として返ってきますので、getメソッドやorElseメソッドなどを使用して中身を取得する必要があります。(選択肢5)
または、要素をまとめるための最初のオブジェクトを第一引数に渡すようにすれば、結果はストリームの要素の型で直接返ってきます。(選択肢4)
よって、選択肢4と5が正解となります。
次のコードを確認してください。
public class Test {
public static
void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("A", "a");
map.compute("A", (s1, s2) -> s1.toLowerCase());
map.computeIfAbsent("B", (s) -> s.toLowerCase());
map.computeIfPresent("A", (s1, s2) -> s1.toLowerCase());
System.out.println(map);
}
}
このコードを実行すると、どのような結果になりますか。
1.
{A=a}
2.
{a=a}
3.
{A=a, B=b}
4.
{A=A, B=b}
5.
{a=a, B=b}
6.
実行時に例外がスローされる。
解答: 3
解説:
JavaSE8で拡張されたMapインタフェースのメソッドに関する問題です。
computeメソッドは、第一引数のKeyでマップされている要素を、第二引数のBiFunctionオブジェクトで変換した値に置き換えます。
BiFunctionオブジェクトの第一引数は、現在のマッピングのKeyで、第二引数がValueになります。
ここでは、Keyが"A"でマッピングされているMap要素を"A"の小文字で置き換えているので、この時点でMapオブジェクトの要素は{A=a}になります。
次に、computeIfAbsentメソッドですが、これは第一引数のKeyでマップされている要素がMapオブジェクトにまだ存在しない場合に、第二引数のFunctionオブジェクトを使用して新たな要素をマッピングします。
このFunctionオブジェクトの引数は、computeIfAbsentの第一引数になりますので、この時点でMapオブジェクトの要素は{A=a, B=b}になります。
最後に、computeIfPresentメソッドですが、これは第一引数のKeyでマップされている要素がMapオブジェクトに存在してる場合に、第二引数のBiFunctionオブジェクトで変換した値に置き換えます。
BiFunctionオブジェクトの第一引数は、現在のマッピングのKeyで、第二引数がValueになります。
ここでは、Keyが"A"でマッピングされているMap要素を"A"の小文字で置き換えているので、この時点でMapオブジェクトの要素は{A=a, B=b}になります。
よって、選択肢3が正解となります。
次のファイルが存在しており、アクセス可能であるとします。
/bucket/brick.txt
/bucket/glass/water.txt
次のコードも確認してください。
Stream<Path> stream1 =
Files.list(Paths.get("/bucket"));
stream1.forEach(System.out::println);
Stream<Path> stream2 =
Files.walk(Paths.get("/bucket"));
stream2.forEach(System.out::println);
ディレクトリ内にソフト・リンクもシンボリック・リンクもない場合、このコードを実行すると、どのような結果になりますか。
1.
bucket/brick.txt
bucket/glass
bucket/brick.txt
bucket/glass
2.
bucket/brick.txt
bucket/glass
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
3.
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
4.
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
bucket/brick.txt
bucket/glass
解答: 2
解説:
ストリームAPIのファイル操作に関する問題です。
Filesクラスのlistメソッドは、引数で指定されたパスの直下にあるファイルやディレクトリを要素に含むStreamオブジェクトを返します。
このとき、直下のディレクトリの、そのさらに下にある要素については含まれません。(再帰的ではありません)
Filesクラスのwalkメソッドは、引数で指定されたパスを開始点として、その下にあるファイルやディレクトリを再帰的にすべて含むStreamオブジェクトを返します。
このとき、開始点となるディレクトリも含まれるため、bucketディレクトリも含まれることになります。
以上をまとめると
・listメソッドのStreamオブジェクトに含まれる要素
bucket/brick.txt
bucket/glass
・walkメソッドのStreamオブジェクトに含まれる要素
bucket
bucket/brick.txt
bucket/glass
bucket/glass/water.txt
となり、選択肢2が正解となります。
次のコードを確認してください。
public class Test {
public static
void main(String[] args) {
List<String> words = Arrays.asList(
"I
love you",
"Ich liebe dich",
"Te quiero"
);
Stream<String> strm = words.stream();
long count
=
// line 1
System.out.println(count);
}
}
このコードを正常にコンパイルし、5と出力するためには、どのコードをline 1に挿入しますか。
1.
strm.flatMap(s
-> Stream.of(s.split(" ")))
.filter(s
-> s.contains("I") || s.contains("i"))
.count();
2.
strm.map(s ->
Stream.of(s.split(" ")))
.filter(s
-> s.contains("I") || s.contains("i"))
.count();
3.
strm.filter(s
-> s.contains("I") || s.contains("i"))
.count();
4.
strm.filter(s
-> s.contains("I") || s.contains("i"))
.flatMap(s
-> Stream.of(s.split(" ")))
.count();
5.
strm.filter(s
-> s.contains("I") || s.contains("i"))
.map(s
-> Stream.of(s.split(" ")))
.count();
解答: 1
解説:
mapメソッドとflatMapメソッドの違いに関する問題です。
mapメソッドは、引数に渡した処理(Function型)によって、ストリームの要素を置き換えます。
flatMapメソッドは、引数に渡した処理(Function型)で要素をStreamオブジェクトに変換し、そのすべてのStreamオブジェクトをひとつにまとめたストリームを生成します。
選択肢1は、各文字列を半角スペースで区切った文字列の配列とし、それらを要素とするストリームになります。(つまり、単語を要素とするストリーム)
次に、I もしくは i を含む要素(単語)だけ抜き出してcountメソッドで数を数えますので、5となりこれが正解です。
選択肢2は、まずmapメソッドで文字配列のストリームに変換しています(要素のひとつ目が{"I", "Love", "You"}になるような配列を要素とするストリーム)。
次にその要素に対してcontainsメソッドを呼んでいるのですが、配列に対してこのメソッドを呼ぶことはできないのでコンパイルエラーとなります。
選択肢3は、ストリームの要素の文字列(つまり、"I love you"などのフレーズ)に対してcontainsメソッドを呼んでおり、3つの要素とも I もしくは i を含むために、次のcountメソッドの結果として3が返ってきてしまいます。
選択肢4は、選択肢3と同様にストリームの要素の配列すべてがfilterメソッドを通過したあとでflatMapメソッドを呼んでいるので、すべての単語が要素となるストリームに変換されます。
すべての単語が要素になっていますので、countメソッドの戻り値が8となり、これも設問の趣旨を満たしません。
選択肢5は、選択肢3や4と同様にストリームの要素の配列すべてがfilterメソッドを通過したあとmapメソッドが続いています。
このmapメソッドは、選択肢2と同様に文字列を空白で区切った文字配列に変換しているだけで、文字配列が3つ含まれていますのでcountメソッドの戻り値は3です。
次のコードを確認してください。
interface Phone {
static void
ring() {
System.out.println("Ring-a-Ring");
}
default void
call(String number) {
System.out.println("Call for " + number);
}
}
interface Mobile extends Phone {}
class OraclePhone implements Mobile {
public void
browse(String url) {
System.out.println("Browsing " + url);
}
}
public class Test {
public static
void main(String[] args) {
OraclePhone
op = new OraclePhone();
Mobile mb =
op; // line 1
mb.call("987-654-321"); // line 2
Mobile.ring(); // line
3
}
}
このコードを実行すると、どのような結果になりますか。
1.
以下のように出力される。
Call
for 987-654-321
Ring-a-Ring
2.
line 1でコンパイル・エラーが発生する。
3.
line 2でコンパイル・エラーが発生する。
4.
line 3でコンパイル・エラーが発生する。
解答: 4
解説:
JavaSE8から導入されたインタフェースのstaticメソッドとdefaultメソッドに関する問題です。
OraclePhoneオブジェクトを、OraclePhoneクラスが実装しているMobile型に暗黙的にキャストすることは、問題ありません。(line 1)
Mobileインタフェースの型で、Mobileインタフェースやその親インタフェースであるPhoneインタフェースに定義されている抽象メソッドおよびデフォルトメソッドを呼び出すことができます。(line 2)
逆に、staticメソッドについては、定義しているインタフェースからしか呼び出すことはできないため、line 3はPhone.ring();としなければコンパイルエラーになってしまいます。(line 3)
よって、line 3でコンパイルエラーが発生し、選択肢4が正解となります。
2017年は、マドリッド(スペイン)の夏時間は3月26日の午前2時に始まります。この結果、午前2時が午前3時になります。
次のコードを確認してください。
ZoneId zid = ZoneId.of("Europe/Madrid");
ZonedDateTime dtFrom = ZonedDateTime.of(
LocalDateTime.of(2017, 3, 26, 0, 0), zid);
ZonedDateTime dtTo = dtFrom.plusHours(3);
DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("H:mm - ");
System.out.print(formatter.format(dtTo));
System.out.println(
ChronoUnit.HOURS.between(dtFrom, dtTo));
このコードを実行すると、どのような結果になりますか。
1.
3:00 - 3
2.
3:00 - 4
3.
4:00 - 3
4.
4:00 - 4
解答: 3
解説:
JavaSE8から導入された日時APIに関する問題です。
日時APIについては、ZonedやOffsetが付くクラスの特徴についてもしっかりと把握しておくようにしてください。
特に、夏時間(サマータイム)に関する問題も出題されるため、そのルールを理解しておく必要があります。
(なお、夏時間の開始日・終了日や、何時が何時になるのかといった情報は、この設問のように問題文に明記されていますので、暗記するような必要はありません)
ZonedDateTimeオブジェクトで、夏時間を挟む時間操作を行った場合は、以下のようなルールになります。
・ChronoUnit.HOURS.betweenメソッドで算出した時間差:実際の時間差(現実世界で経過した時間と同じイメージ)
・DateTimeFormatterで出力した時刻:時計に表示されているのと同じ時刻
次のコードを確認してください。
public class Test {
public static
void main(String[] args) {
LocalDateTime ldt = LocalDateTime.of(2020, 7, 24, 11, 20);
Instant
startTime = ldt.toInstant(ZoneOffset.UTC)
.truncatedTo(ChronoUnit.HOURS);
System.out.println(startTime);
}
}
このコードを実行すると、どのような結果になりますか。
1.
2020-07-24T11:00:00Z
2.
2020-07-24T11:00:20Z
3.
2020-07-24T00:00:00Z
4.
2020-07-24T
解答: 1
解説:
JavaSE8から導入された日時APIに関する問題です。
LocalDateTimeオブジェクトは、ゾーン情報やオフセット情報を持たない日時を表すもので、以下のような生成方法があります。
・nowメソッドを使用し、システムの現在日時から生成
LocalDateTime ldt1 = LocalDateTime.now();
・ofメソッドを使用し、引数に年月日などを指定して生成
LocalDateTime ldt2 = LocalDateTime.of(2020, Month.JULY,
24, 20, 0);
・parse
LocalDateTime ldt3 =
LocalDateTime.parse("2020-06-24T20:00:00Z");
※"2020-06-24T20:00:00Z"は、ISO-8601フォーマットにもとづいた表記で、末尾のZは協定世界時(UTC)を表します
また、LocalDateTimeクラスのtoInstantメソッドは、標準Javaエポック (1970-01-01T00:00:00Z) からの経過時間を表すInstantオブジェクトに変換するためのメソッドです。
さらに、InstantオブジェクトのtruncatedToメソッドは、それを引数で指定した単位で切り捨てる処理になります。
この設問では、2020年7月24日 11時20分00秒として生成したLocalDateTimeオブジェクトを、Instantオブジェクトに変換し、時間までの単位で切り捨てています。(ゾーンオフセットはUTC)
よって、出力としては、2020年7月24日 11時00分00秒をデフォルトのISO-8601フォーマットにした選択肢1が正解となります。