1

OK、初歩的な質問です。私は SCJP の勉強をしていますが、オブジェクト参照のキャストが間違っていることについて 3 つの質問を受けましたが、これらはすべて同じ誤解を示しているようです。正しい洞察がどうあるべきかを確認したかっただけです。そうですね、質問は次のとおりです。

    1.
1.クラスCodeWalkFour {
2. public static void main(String[] args){
3. 車 c = new Lexus();
4. System.out.print(c.speedUp(30) + " ");
5. レクサス l = new Lexus();
6. System.out.print(l.speedUp(30, 40, 50));
7。 }
8.}
9. 車のクラス {
10. プライベート int i=0;
11. int speedUp(int x){
12.私を返します。
13.}
14.}
15.クラスレクサスは車を拡張します{
16. プライベート int j = 1;
17. プライベート int k = 2;
18. int speedUp(int y){
19. j を返します。
20.}
21. int speedUp(int... z){
22. k を返します。
23.}
24.}

3 行目以降の c は Lexus ではなく Car であると考えたため、Lexus.speedUp メソッドではなく、Car.speedUp メソッドが呼び出されます。呼ばれるのは後者であることがわかりました。

    2.
1. クラス StudentProb {
2. プライベート int 学生 ID = 0;
3. void setStudentID(int sid) {
4. Student_id = sid;
5. System.out.println("生徒IDを" + sidに設定しました);
6.}
7. public static void main(String args[]) {
8. int i = 420;
9.オブジェクトob1;
10. StudentProb st1 = new StudentProb();
11. ob1 = st1;
12. st1.setStudentID(i);
13.}
14.}

同じ問題。11 行目で st1 が StudentProb ではなく Object になると思いました。コンパイラはどのようにして setStudentID を見つける場所を知るのでしょうか?

    3.
1. レクチャーホール lh = new レクチャーホール();
2.講堂a1;
3. 施設 f1;
4.
5. f1 = lh;
6. a1 = f1;

施設はインターフェースです。クラス ClassRoom は施設を実装し、Auditorium と LectureHall は ClassRoom のサブクラスです。同じ質問: 5 行目以降、f1 と lh の両方が LectureHall になると思いました。しかし、f1 はまだ施設です。では、ここでキャストは正確に何をしますか?

皆さんありがとう!

PS: どういうわけか、コードの書式設定が機能しません。自由に編集してください。

4

5 に答える 5

2

実行時に、すべてのオブジェクトは自身のクラス、つまり実際に作成されたクラスを認識しています。そのクラスまたは任意のスーパークラスの変数に割り当てることができます。関数を実行すると、オブジェクト参照を保持する変数が宣言されたクラスではなく、オブジェクトが作成されたクラスの関数の「バージョン」が取得されます。

つまり、車/レクサスの例を考えてみましょう。「Lexus mycar=new Lexus(); mycar.speedUp();」と記述した場合、実行されるのは Car.speedUp ではなく、Lexus.speedUp です。たぶんそれは明らかです。しかし、「Car mycar=new Lexus(); mycar.speedUp();」と書いても 実行されるのは依然として Lexus.speedUp です。これは実際のオブジェクトのクラスであるためです。オブジェクトを異なるクラスの異なる変数に好きなように再割り当てできますが、オブジェクトはその「実際の」クラスを認識しています。

基本的には、各オブジェクトが独自のクラス型を保持する隠し変数を持っていると考えてください。これは、実行する関数を見つけるために使用するものです。

コンパイル時には、コンパイラは特定のオブジェクトのクラスを知りません。あなたが書いた場合のように:

void speed1(Car somecar)
{
  somecar.speedUp(1);
}

コンパイラは、ここにある Car が Lexus なのか Honda なのか、あるいは何なのかを知りません。この関数がどこでどのように呼び出されるかがわからないため、それが車であることを知っているだけです。実際の車種は実行時までわかりません。

これが意味することは、次のように書こうとした場合です。

void speed1(Object somecar)
{
    somecar.speedUp(1);
}

コンパイラはこれでエラーを出します。Object が Car であることを知る方法がないため、speedUp が有効な関数であることを知りません。

あなたが書いたとしても:

Object mycar=new Lexus();
mycar.speedUp(1);

エラーが発生します。人間がコードを読むと、mycar が Lexus であり、したがって Car であることが容易にわかりますが、コンパイラは、mycar が Object として宣言されていることを認識し、Object には speedUp 関数がありません。(十分に賢いコンパイラなら、この些細な例で、mycar が Lexus に違いないことを理解できると思いますが、コンパイラは、時々またはほとんどの場合、知っているかもしれないことを処理できず、絶対値で処理する必要があります。)

編集:コメントの質問への回答。

再質問 3: ここでどこが混乱しているのかわかります。COMPILE TIME と RUNTIME を区別する必要があります。

オブジェクトに対して関数を実行すると、そのオブジェクトの「実際の」クラスに固有のその関数の「バージョン」が取得されます。あなたが書いた場合のように:

Car car1=new Lexus();
Car car2=new Chrysler(); // assuming you defined this, of course
car1.speedUp(1);  // executes Lexus.speedUp
car2.speedUp(2);  // executes Chrysler.speedUp

しかし、これはランタイムの問題です。コンパイル時に、コンパイラが認識しているのは、参照を保持する変数の型だけです。これは、オブジェクトの「実際の」クラスと同じである場合もあれば、Object までの任意のスーパークラスである場合もあります。上記のどちらの場合も、それは Car です。Car は speedUp 関数を定義しているため、car1.speedUp の呼び出しは有効です。しかし、肝心な点は次のとおりです。Java はコンパイル時に Car に speedUp 関数があることを認識しているため、Car オブジェクトに対して speedUp を呼び出すことは正当です。しかし、どのスピードアップ機能が得られるかはわかりません。つまり、car2.speedUp と言うと、宣言された型であるため、Java は car2 が Car であることを認識します。実行時ではなく、コンパイル時に言っていることを思い出してください。Lexus なのか Chyrsler なのかはわかりません。単に車であるということだけです。これ'

于 2010-09-01T17:40:25.120 に答える
2

オブジェクトは常に特定のクラスのインスタンスです。任意のスーパー クラスを使用してインスタンスを参照できますが、インスタンスは変更されません。実際のクラスob1.setStudentID(i);ObjectStudentProb

3 番目のスニペットでは、Facilities は Auditorium のスーパークラスであるため、5 行目は有効ではありません。そのため、Auditorium インスタンスを Facilities 変数に割り当てることはできますが、その逆はできません。

于 2010-09-01T16:42:33.997 に答える
1

これは私を助けます - それはあなたを助けないかもしれませんが、私はそれを捨てます.

オブジェクトをキャストすることを、そのオブジェクトに新しい服を着せるように視覚化します。見た目は異なりますが、どんな服を着ても同じオブジェクトです。

たとえば、String を取得して Object にキャストすると、オブジェクトにはなりませんが、オブジェクトの服を着ます。そのメソッドは「オブジェクトの」衣服には存在しないため、オブジェクトで .length を呼び出すことはできませんが、.equals または .hashCode を呼び出すと、文字列で呼び出した場合とまったく同じ答えが得られます。それらは服を通して下にあるオブジェクトを呼び出します。

服は、そのクラスで利用できないメソッドを隠しているだけです。オブジェクト o=新しい文字列(); オブジェクトのように見せるために、新しい服の下にあるオブジェクトではなく文字列にあるすべてのメソッドを非表示にします。

後でオブジェクトを再び文字列としてドレスアップできます。

あなたが着ている服はオブジェクトの操作には関係なく、外の世界がオブジェクトとどのように相互作用するか、またはオブジェクトを見るかだけに関係があります。

これはアナロジーを少し遠ざけているかもしれませんが、「服」にオブジェクトに存在しないメソッドがある場合、それらの服を着ようとしても意味がないので注意してください。 :

String s=new Object();

Object は String のスーツを埋めることができないため、String のスーツには、"length" と "append" のための穴と、Object が単純に埋めることができない他の多くのメソッドがあります。たとえば、手のない人が手袋をはめようとしているようなものです。

于 2010-09-01T17:44:23.590 に答える
1

オブジェクトは常に同じままです。他の型の別の変数に割り当てた場合、元の型にキャストし直さないと、特別なメンバーにアクセスできません。

使用している変数の型のメンバーとメソッドにのみアクセスできます。ただし、この変数によって参照されるオブジェクトは、より特殊な型を持つことができます。オブジェクトは独自の型を格納するため、ランタイムはその実際の型を識別できます。

于 2010-09-01T16:38:02.503 に答える