Domanda

In Java, supponiamo che io abbia 3 classi, C si estende da B che si estende da A.

class X {
   interface A {}
   interface B extends A {}
   interface C extends B {}
   void f(A a) {}

   void test() {
      C c = new C()
      B b = (B) c;

      f(b);
   }
}

Se faccio qualcosa del genere come mostrato in test () sopra:

C c = new C()
B b = (B) c;

f(b);

f () accetta b come tipo C poiché C e B entrambi si estendono da A . Volevo f () per ricevere b come tipo B e non digitare C .

Esiste un modo per forzare questo upcasting?

È stato utile?

Soluzione

f () sempre riceverà qualcosa digitato come A (nonostante sotto le copertine sia in realtà una B o C, e può essere abbattuto in modo appropriato).

Puoi definire un'ulteriore f () così

f(B b);

e se necessario

f(C c);

e quello corretto verrà chiamato in base alla classe dell'argomento. cioè il compilatore determina quale funzione viene chiamata in base al tipo di argomento. Ciò è diverso da un dispacciamento dinamico (o polimorfismo) che si verificherebbe in fase di esecuzione.

Nota che il tuo cast nella domanda è ridondante. Puoi scrivere:

C c = new C()
B b = c;

f(b);

poiché C si estende da B, C è un B.

Altri suggerimenti

Sembra che tu sia confuso sulla differenza tra il tipo di tempo di compilazione e il tipo di runtime.

Stai creando un oggetto (indicato dai riferimenti c e b) di tipo C e rimarrà una C, perché è impossibile cambiare il tipo di runtime di un oggetto e quindi il comportamento; lanciando puoi semplicemente cambiare il suo tipo di tempo di compilazione, che influenza il modo in cui il compilatore lo tratta.

Puoi fornire qualche informazione in più sul problema concreto che stai cercando di risolvere? Molto probabilmente c'è un modo per raggiungere il tuo obiettivo cambiando il design.

  

Volevo che f ricevesse b come tipo B e non come tipo C.

Un C è un B è un A .

All'interno di f , f vede solo la parte A del suo parametro a If f chiama una funzione A pubblica che viene sovrascritta in C , viene chiamata la sostituzione C .

Ecco come funzionano le funzioni virtuali. L'idea è che l'oggetto a cui si fa riferimento è in realtà un C , quindi dovrebbe presentare un comportamento C . Se desideri un comportamento B , passa un'istanza B , non un'istanza C .

Se 'C' e " B 'devono avere lo stesso comportamento, non superare questo comportamento in C .

Perché vuoi farlo?

La tua domanda non ha senso. Che cosa intendi con "f accetta b come tipo C"?

f accetta b come tipo A, poiché la firma del metodo dice "A". Se invochi metodi su b, verranno invocati su C se C li sovrascrive. Ma questo è un comportamento standard in Java (tutti i metodi sono come i metodi virtuali in C ++) e non c'è modo di cambiarlo.

Forse puoi descrivere il tuo vero problema, quindi potremmo essere in grado di aiutarti.

Il fatto che sia possibile passare qualsiasi sottoclasse di A come parametro di f (A a) è inerente a OO in Java, non c'è modo di aggirarlo. Se C estende A, puoi sempre usarlo dove è prevista una A.

puoi usare la riflessione per verificare se il parametro è di classe A:

public void f(A a) {
  if (a.getClass() == A.class) {
    // ok, go on
  }
  else {
    System.err.println("The parameter is not of class A!");
  }
}

non so se è quello che vuoi, ma potrebbe essere utile.

Il problema può essere risolto comprendendo la differenza tra il tipo di RIFERIMENTO e il tipo di ISTANZA. Quando lanci l'oggetto C come B, cambi solo il tipo di riferimento - l'istanza effettiva dell'oggetto è ancora un oggetto C - questo dovrebbe essere abbastanza ovvio se guardi l'oggetto in modalità debug. La modifica del tipo di riferimento influisce solo sul modo in cui il compilatore gestisce / percepisce il codice - il comportamento in fase di esecuzione non dovrebbe essere influenzato. Qualsiasi chiamata di metodo su un oggetto C invocherà sempre il metodo C (indipendentemente dal fatto che l'oggetto sia stato o meno trasmesso a qualcos'altro). Se esegui l'overode di un metodo da B e desideri invocare la versione del metodo B, dovrai creare un'istanza B.

Il fatto che il tuo codice abbia un riferimento a una A non significa che anche l'oggetto puntato sia una A. Se è una a C rimane una C. Il riferimento nella tua fonte limita solo i metodi disponibili nella tua fonte. Il cast è necessario solo perché a volte è necessario un metodo su un tipo diverso e dobbiamo ingannare il compilatore in modo da iniziare a utilizzare i metodi sui cast per digitare. Naturalmente il cast può fallire in fase di esecuzione se il tentativo non è valido.

In upcasting and downcasting the  object first upcast then downcastenter
class A
{
}
class B extends A
{
}
Class M
{
  psvm(String ar[])
{

   A a1= new B();
   B b2=(B)a1;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top