class Dad
{
protected static String me = "dad";
public void printMe()
{
System.out.println(me);
}
}
class Son extends Dad
{
protected static String me = "son";
}
public void doIt()
{
new Son().printMe();
}
関数doItは「dad」を出力します。 「息子」と印刷する方法はありますか?
はい。しかし、変数が関係しているので、それは上書きされます(変数に新しい値を与える。関数に新しい定義を与えることはオーバーライドです)。Just don't declare the variable but initialize (change) in the constructor or static block.
親クラスのブロックで使用する場合、値は反映されます
変数が静的な場合、初期化中に静的ブロックを使用して値を変更します。
class Son extends Dad {
static {
me = 'son';
}
}
または、コンストラクターを変更します。
また、ブロックの後で値を変更することもできます。スーパークラスに反映されます
要するに、いや、クラス変数をオーバーライドする方法はありません。
Javaのクラス変数をオーバーライドしないでください。オーバーライドは、たとえばメソッドです。非表示は上書きとは異なります。
指定した例では、クラスSonで名前 'me'のクラス変数を宣言することにより、同じ名前 'me'のスーパークラスDadから継承したクラス変数を非表示にします。この方法で変数を非表示にしても、スーパークラスのお父さんのクラス変数「me」の値には影響しません。
質問の2番目の部分、つまり「息子」と表示する方法については、コンストラクターを使用して値を設定します。以下のコードは元の質問からかなり離れていますが、私はこのように書きます。
public class Person {
private String name;
public Person(String name) {
this.name = name;
}
public void printName() {
System.out.println(name);
}
}
JLSでは、セクション 8.3-フィールド宣言 の非表示に関する詳細が提供されます。
はい、単にprintMe()
メソッドをオーバーライドします:
class Son extends Dad {
public static final String me = "son";
@Override
public void printMe() {
System.out.println(me);
}
}
ゲッターを作成してから、そのゲッターをオーバーライドできます。オーバーライドする変数がそれ自体のサブクラスである場合に特に役立ちます。スーパークラスにObject
メンバーがあるが、サブクラスではInteger
に定義されるようになりました。
class Dad
{
private static final String me = "dad";
protected String getMe() {
return me;
}
public void printMe()
{
System.out.println(getMe());
}
}
class Son extends Dad
{
private static final String me = "son";
@Override
protected String getMe() {
return me;
}
}
public void doIt()
{
new Son().printMe(); //Prints "son"
}
あなたがそれをオーバーライドしようとしている場合、これを静的に保つ正当な理由はわかりません。抽象化の使用をお勧めします(コード例を参照)。 :
public interface Person {
public abstract String getName();
//this will be different for each person, so no need to make it concrete
public abstract void setName(String name);
}
これで、お父さんを追加できます。
public class Dad implements Person {
private String name;
public Dad(String name) {
setName(name);
}
@Override
public final String getName() {
return name;
}
@Override
public final void setName(String name) {
this.name = name;
}
}
息子:
public class Son implements Person {
private String name;
public Son(String name) {
setName(name);
}
@Override
public final String getName() {
return name;
}
@Override
public final void setName(String name) {
this.name = name;
}
}
お父さんはニースの女性に会いました:
public class StepMom implements Person {
private String name;
public StepMom(String name) {
setName(name);
}
@Override
public final String getName() {
return name;
}
@Override
public final void setName(String name) {
this.name = name;
}
}
私たちには家族がいるように見えるので、自分の名前を世界に伝えましょう。
public class ConsoleGUI {
public static void main(String[] args) {
List<Person> family = new ArrayList<Person>();
family.add(new Son("Tommy"));
family.add(new StepMom("Nancy"));
family.add(new Dad("Dad"));
for (Person person : family) {
//using the getName vs printName lets the caller, in this case the
//ConsoleGUI determine versus being forced to output through the console.
System.out.print(person.getName() + " ");
System.err.print(person.getName() + " ");
JOptionPane.showMessageDialog(null, person.getName());
}
}
}
System.out出力:トミーナンシーパパ
System.errは上記と同じです(赤色のフォントのみ)
JOption出力:
Tommy then
ナンシーその後
パパ
これは設計上の欠陥のように見えます。
静的キーワードを削除し、たとえばコンストラクターで変数を設定します。このようにして、Sonはコンストラクターで変数を異なる値に設定するだけです。
クラス変数はサブクラスでのみ非表示になり、オーバーライドされないことは事実ですが、サブクラスでprintMe ()
をオーバーライドせずに必要なことを実行することは可能です。以下のコードでは、明確にするために例外処理を省略しています。 me
をprotected
として宣言することは、サブコンテキストで非表示になるため、このコンテキストではあまり意味がないようです。
class Dad
{
static String me = "dad";
public void printMe ()
{
Java.lang.reflect.Field field = this.getClass ().getDeclaredField ("me");
System.out.println (field.get (null));
}
}
class Dad
{
protected static String me = "dad";
public void printMe()
{
System.out.println(me);
}
}
class Son extends Dad
{
protected static String _me = me = "son";
}
public void doIt()
{
new Son().printMe();
}
...「息子」を印刷します。
printMe()
をオーバーライドすることによってのみ:
class Son extends Dad
{
public void printMe()
{
System.out.println("son");
}
}
Dad.printMe
メソッド内のme
への参照は暗黙的に静的フィールドDad.me
を指しているため、何らかの方法でprintMe
でSon
が行うことを変更しています...
クラス内の変数をオーバーライドすることはできません。メソッドのみをオーバーライドできます。変数を非公開にしておくと、多くの問題が発生する可能性があります。
変数は上書きに関与しません。メソッドのみが行います。メソッド呼び出しは実行時に解決されます。つまり、メソッドを呼び出す決定は実行時に行われますが、変数はコンパイル時にのみ決定されます。したがって、その参照はランタイムオブジェクトではなく呼び出しに使用される変数が呼び出されます。
次のスニペットをご覧ください。
package com.demo;
class Bike {
int max_speed = 90;
public void disp_speed() {
System.out.println("Inside bike");
}
}
public class Honda_bikes extends Bike {
int max_speed = 150;
public void disp_speed() {
System.out.println("Inside Honda");
}
public static void main(String[] args) {
Honda_bikes obj1 = new Honda_bikes();
Bike obj2 = new Honda_bikes();
Bike obj3 = new Bike();
obj1.disp_speed();
obj2.disp_speed();
obj3.disp_speed();
System.out.println("Max_Speed = " + obj1.max_speed);
System.out.println("Max_Speed = " + obj2.max_speed);
System.out.println("Max_Speed = " + obj3.max_speed);
}
}
コードを実行すると、コンソールに以下が表示されます。
Inside Honda
Inside Honda
Inside bike
Max_Speed = 150
Max_Speed = 90
Max_Speed = 90
フィールドはオーバーライドされず、隠されているため、実際には「dad」と表示されます。 「息子」を印刷するには、3つの方法があります。
アプローチ1:printMeをオーバーライドする
class Dad
{
protected static String me = "dad";
public void printMe()
{
System.out.println(me);
}
}
class Son extends Dad
{
protected static String me = "son";
@override
public void printMe()
{
System.out.println(me);
}
}
public void doIt()
{
new Son().printMe();
}
アプローチ2:フィールドを非表示にしてコンストラクターで初期化しない
class Dad
{
protected static String me = "dad";
public void printMe()
{
System.out.println(me);
}
}
class Son extends Dad
{
public Son()
{
me = "son";
}
}
public void doIt()
{
new Son().printMe();
}
アプローチ3:静的な値を使用してコンストラクターのフィールドを初期化する
class Dad
{
private static String meInit = "Dad";
protected String me;
public Dad()
{
me = meInit;
}
public void printMe()
{
System.out.println(me);
}
}
class Son extends Dad
{
private static String meInit = "son";
public Son()
{
me = meInit;
}
}
public void doIt()
{
new Son().printMe();
}
https://docs.Oracle.com/javase/tutorial/Java/IandI/hidevariables.html
非表示フィールドと呼ばれます
上記のリンクから
クラス内では、スーパークラスのフィールドと同じ名前のフィールドは、タイプが異なっていてもスーパークラスのフィールドを隠します。サブクラス内では、スーパークラスのフィールドは単純名で参照できません。代わりに、次のセクションで説明するスーパーを介してフィールドにアクセスする必要があります。一般的に、コードを読みにくくするため、フィールドを非表示にすることはお勧めしません。
もちろん、プライベート属性を使用し、ゲッターとセッターを行うことをお勧めしますが、次をテストしましたが、動作します...コード内のコメントを参照してください
class Dad
{
protected static String me = "dad";
public void printMe()
{
System.out.println(me);
}
}
class Son extends Dad
{
protected static String me = "son";
/*
Adding Method printMe() to this class, outputs son
even though Attribute me from class Dad can apparently not be overridden
*/
public void printMe()
{
System.out.println(me);
}
}
class Tester
{
public static void main(String[] arg)
{
new Son().printMe();
}
}
すっごく...継承のルールを再定義しただけなのか、Oracleをトリッキーな状況に陥らせたのか?このプログラムを実行するとわかるように、保護された静的String meは明らかにオーバーライドされます。また、属性をオーバーライドできない理由は、私には意味がありません。
サブクラスで変数を簡単に再割り当てできるのに、なぜ変数をオーバーライドしたいのでしょうか。
このパターンに従って、言語設計を回避します。フレームワークに、複数の派生アプリケーションの異なるフレーバーで使用する必要がある重いサービスクラスがある場合を想定します。その場合、スーパークラスロジックを構成する最良の方法は、「定義」変数を再割り当てすることです。
public interface ExtensibleService{
void init();
}
public class WeightyLogicService implements ExtensibleService{
private String directoryPath="c:\hello";
public void doLogic(){
//never forget to call init() before invocation or build safeguards
init();
//some logic goes here
}
public void init(){}
}
public class WeightyLogicService_myAdaptation extends WeightyLogicService {
@Override
public void init(){
directoryPath="c:\my_hello";
}
}
いいえ。クラス変数(インスタンス変数にも適用可能)は、Javaのオーバーライド機能を示しません。クラス変数は、呼び出し元オブジェクトのタイプに基づいて呼び出されるためです。より明確にするために、階層にもう1つのクラス(人間)を追加しました。だから今
息子がパパを広げる
次のコードでは、Human、Dad、およびSonオブジェクトの配列を反復処理しようとしますが、呼び出しオブジェクトのタイプがHumanであるため、すべての場合にHuman Classの値を出力します。
class Human
{
static String me = "human";
public void printMe()
{
System.out.println(me);
}
}
class Dad extends Human
{
static String me = "dad";
}
class Son extends Dad
{
static String me = "son";
}
public class ClassVariables {
public static void main(String[] abc) {
Human[] humans = new Human[3];
humans[0] = new Human();
humans[1] = new Dad();
humans[2] = new Son();
for(Human human: humans) {
System.out.println(human.me); // prints human for all objects
}
}
}
印刷します
したがって、クラス変数のオーバーライドはありません。
親クラスの参照変数から実際のオブジェクトのクラス変数にアクセスする場合、親参照(ヒューマンオブジェクト)をその型にキャストすることにより、明示的にコンパイラーに通知する必要があります。
System.out.println(((Dad)humans[1]).me); // prints dad
System.out.println(((Son)humans[2]).me); // prints son
印刷します
この質問の一部について:-すでに提案されているように、SonクラスのprintMe()メソッドをオーバーライドしてから呼び出します
Son().printMe();
お父さんのクラス変数「me」は非表示になります。「me」(Sonクラス内)の最も近い宣言(Sonクラスのprintme()メソッドから)が優先されるためです。