質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.40%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

1回答

351閲覧

Javaのsealedクラスに関して、switch文で分岐して処理をした際にわからないことがあったので質問いたします。

kaguyabito

総合スコア3

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

0クリップ

投稿2024/07/24 05:11

編集2024/07/24 05:13

実現したいこと

『すっきりわかるJava実践編』という書籍にて勉強していたところシールクラスについて記載があり、自分でも試しながら作成していたところ、switch文で子クラスのインスタンスが渡された際の処理を分岐している記述で「defalt句」を使用せずに実現できる方法について記載のあった以下のコードで疑問がありました。

public sealed class Charactor permits Hero,Wizard{} public final class Hero extends Charactor{} public final class Wizard extends Charactor{}

上記継承関係のクラスを作成し

public void heal(Charactor target){ switch(target){ case Hero h ->{h.hp += 20;} case Wizard w -> {w.hp += 5;} } }

というメソッドを作成し分岐させることができ、かつ子クラスはpermitsで指定しているHeroとWizardに限定されるため、default句を使用しなくてもコンパイルエラーは発生しないということだったのですが、自分で試してやってみたところ、コンパイルエラーが発生しました。

発生している問題・分からないこと

コンパイルエラーの原因はswitch文がすべての可能な入力値をカバーしていないというものでしたが、default句もしくは親クラスであるCharactorのインスタンスが渡された際の処理を記述したところエラーは解消しました。
そこで疑問だったのが、書籍では、継承された子クラスのみの分岐処理を記載しシールクラスの有用性を説明しており、便利な機能だなと自分もそれで納得していたのですが、親クラスのインスタンスを渡した際の処理を書かなければならないならば、書籍もそういう記載にしていたはずだと感じまして、自分で勝手に作ったクラス、もしくはhealメソッドの記載場所等にに何か誤りがあるかといろいろやってみたのですが、コンパイルは一向に通らず、このケースではやはり親クラスのインスタンスを渡した際の処理の記述の必要があるのでしょうか。また、子クラスのインスタンスが渡ってきた際の処理の記述のみでOKな方法はあるのでしょうか。
ご教示しただけますと幸いです。
よろしくお願いいたします。

エラーメッセージ

error

1Main.java:20: エラー: switch文がすべての可能な入力値をカバーしていません

該当のソースコード

特になし

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

public

1 public static void main(String[] args) { 2 Animal dog = new Dog(10,"Dog"); 3 Animal cat = new Cat(10,"Cat"); 4 Bird bird = new Bird(10,"Bird"); 5 Animal animal = new Animal(); 6 7 cat.heal(cat); 8 System.out.println(cat); 9 animal.heal(animal); 10 // bird.heal(bird); 11 // System.out.println(bird); 12 13 } 14 15} 16 17sealed class Animal permits Dog,Cat{ 18 public void heal(Animal target){ 19 switch (target) { 20 case Dog d->{d.hp += 20;} 21 case Cat c->{c.hp += 100;} 22 //case Animal a ->{System.out.println("too bad");} 23 } 24 } 25 26} 27 28final class Dog extends Animal{ 29 int hp; 30 String name; 31 public Dog(int hp,String name){ 32 this.hp = hp; 33 this.name = name; 34 } 35 @Override 36 public String toString(){ 37 return this.name+":"+this.hp; 38 } 39} 40final class Cat extends Animal{ 41 int hp; 42 String name; 43 public Cat(int hp,String name){ 44 this.hp = hp; 45 this.name = name; 46 } 47 @Override 48 public String toString(){ 49 return this.name+":"+this.hp; 50 } 51} 52// Animalにpremitsされていないため、Animal継承不可 53final class Bird{ 54 int hp; 55 String name; 56 public Bird(int hp,String name){ 57 this.hp = hp; 58 this.name = name; 59 } 60 61 @Override 62 public String toString(){ 63 return this.name+":"+this.hp; 64 } 65}

補足

特になし

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2024/07/24 08:49 編集

「エラーメッセージ」の出た時のコードを「該当のソースコード」に載せて頂けますか。 また、コンパイルに使った java のバージョン等もあると良いかもしれません。(特定のバージョンでバグがあったとかの可能性とか)
1121

2024/07/24 08:56

「エラーメッセージ」 Main.java:20: エラー: switch文がすべての可能な入力値をカバーしていません とご記載があります。 > コンパイルエラーの原因はswitch文がすべての可能な入力値をカバーしていないというものでしたが、default句もしくは親クラスであるCharactorのインスタンスが渡された際の処理を記述したところエラーは解消しました。 と、エラーメッセージの内容を解消しているようですが > コンパイルは一向に通らず とあり修正したソースコードとエラーメッセージは何になるのか私には読み取れませんでした paiza cloud で試してみたのですが違いをおしえてもらえないでしょうか https://paiza.io/projects/6xaB6e7w6XAqyYWJepfYgA ※書き換えてもらって大丈夫です。構文エラーが出る状態が望ましいです。細かい制限はあります。。。
退会済みユーザー

退会済みユーザー

2024/07/24 09:36

あんまりちゃんと読んだり検証してないのでコメントで。 JDK21で入ったPattern Matching for switch Expressions and Statementsは以下のとおりです。 https://docs.oracle.com/en/java/javase/21/language/pattern-matching-switch-expressions-and-statements.html ここで参照される https://openjdk.org/jeps/441 にある以下の記述がその理由かと思います。ようはコンパイルした後で変わる可能性があるからです。 Again, the notion of exhaustiveness is an approximation. Because of separate compilation, it is possible for a novel implementation of the interface I to show up at runtime, so the compiler will in this case insert a synthetic default clause that throws. 実際複数ファイルに分けてインスタンス生成も分けてビルドすると、switch文を含むjavaファイルをコンパイルせずにsealed classを変更して、その新しいクラスのインスタンスを生成して渡せます。単純にはnullを渡すだけでも必要になりますね。抽象クラスやinterfaceでは違うチェックになるようで、defaultは不要になり、同じ操作をすると実行時例外になります。
退会済みユーザー

退会済みユーザー

2024/07/24 09:49

一応Unix系用ですが検証用コードを載せておきます。 cat >Main.java <<EOF public class Main { private static void hoge(Charactor target) { switch(target){ case Hero h ->{System.out.println("Hero");} case Wizard w -> {System.out.println("Wizard");} //case null -> {System.out.println("null");} default ->{System.out.println("default");} } } public static void main(String[] args) { hoge(new Factory().create()); } } EOF cat >Charactor.java <<EOF public sealed class Charactor permits Hero,Wizard{} //public abstract sealed class Charactor permits Hero,Wizard{} //public sealed class Charactor permits Hero,Wizard,Warrior{} //public abstract sealed class Charactor permits Hero,Wizard,Warrior{} EOF cat >Hero.java <<EOF public final class Hero extends Charactor{} EOF cat >Wizard.java <<EOF public final class Wizard extends Charactor{} EOF cat >Warrior.java <<EOF public final class Warrior extends Charactor{} EOF cat >Factory.java <<EOF public class Factory { public Charactor create() {return new Hero();} //public Charactor create() {return new Warrior();} } EOF javac Main.java java Main ls -lAFrt cat >Charactor.java <<EOF //public sealed class Charactor permits Hero,Wizard{} //public abstract sealed class Charactor permits Hero,Wizard{} public sealed class Charactor permits Hero,Wizard,Warrior{} //public abstract sealed class Charactor permits Hero,Wizard,Warrior{} EOF cat >Factory.java <<EOF public class Factory { //public Charactor create() {return new Hero();} public Charactor create() {return new Warrior();} } EOF javac Factory.java java Main ls -lAFrt
退会済みユーザー

退会済みユーザー

2024/07/24 12:12

色々書きましたが、「コンパイルした後で変わる可能性があるから」というよりは、単純に「(インスタンス化可能な)型を網羅していないから」の方がエラーの整合が取れますね。私の言った理由はdefault分岐コードを書いたら分岐がなさそうでもコード生成される理由と言った方が近いようです。 なお書籍の掲載コードをダウンロードしましたが、sealedを使った記述は見つからず、code15-06に > // Characterクラス。 > // 未来の開発者はこれを継承してHeroやWizardを作る。 > public abstract class Character { という記述が見つかっただけでした。何やら分かりませんが、そもそもabstractっぽい感じですね。
kaguyabito

2024/07/27 04:53

Unix系はまだ勉強しておらず、ご記載いただいたコードも私には理解が及びそうもありませんが ご検証頂いた結果、書籍ではabstractへの言及をしていなかったため、勝手に混乱していたみたいでした。 自分が抜けていたnullへの分岐もご記載頂きありがとうございます。 不勉強で申し訳ございませんが、コメントありがとうございました!
kaguyabito

2024/07/27 05:06

すみません! コメントが折りたたまれていたことに気づかず、全てのコメントを今確認いたしました。jimbe様、1121様もご検討いただきありがとうございました! 回答にあったnanashi123様をベストアンサーにいたしましたがご要望など色々修正できず申し訳ございませんでした。
退会済みユーザー

退会済みユーザー

2024/07/27 07:07

多重継承(Javaはそもそもできない)や実装の継承を嫌う昨今、abstractクラスはそんなに使われないので、多いのは継承構造をガチガチにしないinterfaceだと思いますよ。私もそうですが、回答の方でもextends→implementsが面倒だったのでabstractを使っただけだと思います。
guest

回答1

0

ベストアンサー

入力値というのは、引数に渡されうる全ての値を指すので、その全てのパターンに対応する必要があります
Charactorは子クラスを含むだけでなく、単体でのインスタンス化も可能です
またインスタンスにはnullが想定されるため、これの対応も必要になると思われますが、これは除外しても問題なく、こちらの方が仕様としては不可解な気はします

ともあれ、対策としてはCharactor絶対にインスタンス化されない環境を用意すればいいことになります
Javaにおいて基底型単体のインスタンス生成を禁止する修飾子がabstractです

java

1sealed abstract class Charactor permits Hero,Wizard{ 2  protected int hp=0; 3 4  public static void heal(Charactor target) 5    switch(target){ 6      case Hero h ->{h.hp += 20;} 7      case Wizard w -> {w.hp += 5;} 8    } 9  } 10}

これでCharactorだけが渡るケースを除外することができます
参考書ではおそらくこのパターンを見落としているのでしょう

defaultは全ての例外パターンに対応するブロックです
例外の余地が一切認められない場合には、これを省略することができます
しかし例外を排除することは難しく、例えば

java

1sealed class Charactor permits Hero,Wizard{ 2  protected Charactor(){} 3}

などの一見外部からインスタンス生成することが不可能に思える設計であっても、メソッドの戻り値で単体のインスタンスを補足できてしまう可能性があるので、このケースを除外できません
sealedでパターンを絞れるのは継承先を限定できるからであって、この修飾子特有の恩恵というわけではありません
最も、その設計が難しいのでそれを簡易化する修飾子が提供されています

投稿2024/07/24 08:48

編集2024/07/24 09:26
nanashi123

総合スコア73

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

kaguyabito

2024/07/27 05:08

ご回答ありがとうございます。 そもそも、親クラスのインスタンスをabstractで生成できないようにするんですね! しかし、例外がある旨もご教示いただきありがとうございます。 使用するケースがあれば慎重に検討いたします。 ベストアンサーとさせていただきました。 ありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.40%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問