2010-05-29 19 views
12

Ci sono già diversi SO domande sul perché non v'è il metodo/campo statico astratto come tale, ma mi chiedo su come si potrebbe fare per attuare il seguente pseudo-codice:Metodo "Abstract static": come?

class Animal { 
    abstract static int getNumberOfLegs(); // not possible 
} 

class Chicken inherits Animal { 
    static int getNumberOfLegs() { return 2; } 


class Dog inherits Animal { 
    static int getNumberOfLegs() { return 4; } 

Qui è il problema : Supponendo che voglio assicurarmi che ogni classe che eredita contenga il metodo getNumberOfLegs() (cioè quasi come un'interfaccia, eccetto che io voglio che la classe astratta implementa diversi metodi comuni a tutte le classi figlie, quindi l'interfaccia pura non funziona qui). getNumberOfLegs() ovviamente dovrebbe essere un metodo statico (supponendo che in un mondo perfetto non abbiamo pollo e cani azzoppati quindi getNumberOfLegs non dipende dall'istanza).

Senza un metodo/campo "statico astratto", è possibile lasciare il metodo fuori dalla classe Animal, quindi esiste il rischio che alcune classi figlio non dispongano di tale metodo. Oppure si può fare di un metodo di istanza getNumberOfLegs, ma poi si dovrebbe istanziare una classe per scoprire quante zampe ha quell'animale, anche se non è necessario.

Come si fa di solito per implementare questa situazione?


EDIT: Ecco come potrei usare questa. Supponiamo (ora questo è ridicolo, ma comunque ...) che il numero di gambe di ogni animale è unico, quindi potrei avere qualcosa di simile:

Animal getModelAnimal(int numberOfLegs) { 
    if (numberOfLegs == Chicken.getNumberOfLegs()) return new Chicken(); 
    else if (numberOfLegs == Dog.getNumberOfLegs()) return new Dog(); 
} 

risposta

3

vostro pseudocodice sembrava molto simile a Java, quindi sono assumendo che sia Java che stai usando.

"Un metodo astratto richiede l'implementazione per istanza.I metodi statici appartengono a una classe generale.Un metodo statico in una classe astratta appartiene alla classe astratta, non a potenziali implementazioni.Pertanto non ha senso consentire l'astrazione statica Inoltre, i metodi statici non possono essere sovrascritti, quindi, di nuovo, i metodi statici astratti costituirebbero un'anomalia. "

Da http://forums.sun.com/thread.jspa?threadID=597378

Leggi anche le Why can't I define a static method in a Java interface?

+0

-1: Non si può dire questo in generale. il metodo astratto non è sempre privo di senso (vedi il mio post). – Simon

+0

Modificato la mia risposta per riflettere questo. –

+0

La mia domanda è intesa come indipendente dalla lingua (ho incontrato un problema simile in C# prima). Posso capire perché non c'è un modificatore "statico astratto" in una lingua, ma sono più interessato a vedere come si può implementare in modo pulito il problema che ho affermato nella domanda. – polyglot

0

Anche se si potrebbe avere un metodo statico astratto, come l'usereste? Pensa a come lo useresti prima di pensare a come implementarlo, poiché la tua implementazione deve corrispondere agli usi.

+0

@Downvoter: perché non dire alle persone qual è il problema con la mia risposta? –

3

Questo è davvero un buon punto e talvolta manca abstract static. Tuttavia, poiché al giorno d'oggi la memoria non è un problema, è possibile implementare il metodo getNumberLegs() come metodo di istanza.

Dire che l'astratto statico è non senso, non è vero. PHP consente metodi statici astratti (vedere this) e lo scenario mostra che potrebbe essere utile in alcune situazioni.

Non è inoltre corretto affermare che i metodi static non possono essere sovrascritti; final i metodi non possono essere sovrascritti. Nelle lingue come Java e C#, static viene fornito con final. Ecco perché molti suppongono che static sia uguale a "non sovrascrivibile".

Parlando di C# (dopo aver letto i vostri commenti, presumo che "parli" C#), si può prendere in considerazione di utilizzare farmaci generici e gli attributi (o generici e annotazioni in Java):

public class Animal 
{ 
    public static int GetNumberOfLegs<T>() where T : Animal 
    { 
    //Get T's custom attribute "NumberOfLegs" and return its value 
    } 

    //EDIT: Added runtime-version of GetNumberOfLegs. 
    public static int GetNumberOfLegs(Type t) 
    { 
    //Get t's custom attribute "NumberOfLegs" and return its value 

    } 
} 

[NumberOfLegs(4)] 
public class Cat { ... }; 

Questo vi permetterà di per ottenere il numero di gambe di ciascun tipo senza istanziarlo. Basta ricordare di indicare l'attributo [NumberOfLegs(x)]. Devi anche conoscere il tipo al momento della compilazione (per la versione generica del metodo).

EDIT: ho aggiunto una versione di runtime del GetNumberOfLegs() -Metodo, a cui è possibile passare un oggetto Type (dovrebbe essere Class per Java). In questo caso dovrai eseguire un controllo del tipo in fase di esecuzione, ovvero verificare se il tipo rappresentato dall'oggetto Type -/Class eredita da Animal e quindi recuperare il valore passato nell'attributo/annotazione.

Usage:

int numberOfLegs1 = Animal.GetNumberOfLegs<Cat>(); 
int numberOfLegs2 = Animal.GetNumberOfLegs(typeof(Cat)); //runtime version 
+0

Confrontare Java/C# con PHP è come confrontare mele con arance .. – BalusC

+2

Non è un paragone ... Sto dimostrando che l'elettricità statica astratta non è un senso. Sottolineo un concetto OOP generale. – Simon

+0

scusate, ma di per sé, la statica astratta non ha ancora senso. Avevi bisogno di un attributo personalizzato per dare un senso a questo caso d'uso. –

2

Come uno di solito andare sull'implementazione di questa situazione?

In termini Java, ho appena dichiarato un costruttore nella classe astratta che accetta un argomento fisso. A ogni sottoclasse è quindi richiesto di invocarlo, altrimenti non verrà compilato.

abstract class Animal { 
    private int numberOfLegs; 

    public Animal(int numberOfLegs) { 
     this.numberOfLegs = numberOfLegs; 
    } 

    public int getNumberOfLegs() { 
     return numberOfLegs; 
    } 
} 

class Chicken extends Animal { 
    public Chicken() { 
     super(2); 
    } 
} 

class Dog extends Animal { 
    public Dog() { 
     super(4); 
    } 
} 

Aggiornamento: secondo il vostro aggiornamento

EDIT: Ecco come potrei usare questa. assuma (ora questo è ridicolo, ma comunque ...) che il numero di gambe di ogni animale è unico, quindi potrei avere qualcosa di simile:

Animal getModelAnimal(int numberOfLegs) { 
    if (numberOfLegs == Chicken.getNumberOfLegs()) return new Chicken(); 
    else if (numberOfLegs == Dog.getNumberOfLegs()) return new Dog(); 
} 

Questo è davvero ridicolo, ciò richiede che tutti questi animali concreti siano noti in anticipo nel metodo astratto della fabbrica. Dovresti aggiornare il metodo di fabbrica astratto ogni volta che viene aggiunto un nuovo tipo di animale in calcestruzzo. Qual è il punto della fabbrica astratta allora? Sai già tutto in anticipo? No, lascia che il metodo factory astratto prenda il nome completo della classe qualificata come identificatore o così, in modo che possa provare a caricare dal classpath (ancora parlando in termini Java).

+0

Ma 'getNumberOfLegs()' è ancora un metodo di istanza e devo istanziare le classi per scoprire quante zampe l'animale ha ... – polyglot

+0

Vedere l'aggiornamento della mia risposta. – BalusC

+0

Il metodo 'getModelAnimal()' non è necessariamente definito nella classe Animale, però. – polyglot

0

Questa è una domanda interessante. Secondo me, i metodi "static abstract" sono raramente necessari e ci sono sempre buone alternative.

Nel caso di utilizzo fornito, ad esempio, il metodo factory affronta le classi di animali in calcestruzzo per nome; per ogni nuova classe di animali, dovrebbe essere aggiunto un nuovo codice specifico. Pertanto, sembra che la qualifica "astratta" non sia realmente necessaria. È sufficiente una convenzione per fornire un metodo statico getNumberLegs().

E più in generale, combinando astratta e statica non ha alcun senso (in Java) dal astratta implica il polimorfismo, mentre statiche chiamate non sono polimorfici a tutti, e il lavoro sulle classi noti al momento della compilazione.

4

Come si fa in genere uno implementando questa situazione?

La soluzione usuale consiste nel rendere il metodo in questione un metodo di istanza.

getNumberOfLegs() ovviamente dovrebbe essere un metodo statico (assumendo che in un mondo perfetto noi non' hanno messo in ginocchio pollo e cani in modo getNumberOfLegs è non esempio-dipendente).

Questo è enfaticamente non ovvio! Non programmiamo per un mondo perfetto, e nel mondo reale gli animali a quattro zampe hanno a volte gambe uno, due o tre (o cinque).

Se il programma ha bisogno di animale definizioni piuttosto che animale casi, andare avanti e fare una classe per che.

class AnimalDefinition { 
    public string getScientificName(); 
    public string getCommonName(); 
    public int getNumberOfLegs(); 
    public bool getIsAmphibious(); 
    // etc. 
} 

Poi inizializzare una raccolta di quelli all'inizio del programma - idealmente da un database o file di configurazione in cui è possibile aggiungere le definizioni di animali senza scrivere o compilare un'altra riga di codice. (E si può ottenere via con molti meno tipi.)

0

Si potrebbe cludge un runtime verifica che il metodo è implementato da avere la vostra classe di base un'eccezione nella sua attuazione. Non vale molto, ma forse meglio di niente ...

0

I metodi astratti hanno senso se li chiami in base alla classe base. Nota che nel tuo esempio non stai utilizzando il polimorfismo. Nell'esempio dovrebbe essere qualcosa di simile:

(Animal)Dog.getNumberOfLegs() //cast Dog to Animal first 

Comunque PHP implementa la cosiddetta "vincolante in ritardo statico" che è probabilmente quello che stai cercando per

http://php.net/manual/en/language.oop5.late-static-bindings.php

In C++ una funzionalità simile può essere realizzato utilizzando tamplates e polimorfismo in fase di compilazione.

0

abstract static i metodi hanno senso solo in linguaggi in cui le variabili possono contenere tipi reali e non solo istanze. (Delphi è uno di questi linguaggi, C# non lo è, e non penso che tu possa farlo anche in Java). Il motivo per cui è che se si conosce in fase di compilazione esattamente quali classi si stanno utilizzando (come nel tuo esempio), allora non c'è motivo per il metodo di essere abstract, si potrebbe semplicemente avere i metodi static in ogni classe denominata lo stesso cose. L'unico modo in cui potresti non sapere quali tipi stai usando è se puoi assegnare dei tipi a una variabile, dato che puoi passarli (proprio come le istanze delle classi) e improvvisamente tutto ha effettivamente senso ed è utile.

penso maggior parte dei compilatori/lingue che supportano tipi assegnazione (così come istanze di tipi) a variabili riescono anche a sostenere abstract static e virtual abstract metodi attraverso la magia del compilatore, quindi se sono realmente utili nella lingua di propria scelta poi dovrebbe essere supportato

+0

Non solo lingue con tipi variabili. Un altro caso è quando tale metodo statico astratto viene chiamato da un altro metodo. Diciamo che uno dei metodi di Animal chiama 'Animal.getNumberOfLegs()'. Allora può Cane chiamare in uno dei suoi metodi questo metodo. Poiché 'getNumberOfLegs()' è astratto 'Animal.getNumberOfLegs()' all'interno del corpo del metodo di Animal, potrebbe essere inviato a 'Dog.getNumberOfLegs()'. – doc

+0

Questo esempio non ha senso. Come si suppone il compilatore sapere di inviare una chiamata statica a un altro tipo in base alla firma della chiamata non statica in cui ci si trova attualmente? Non è così che funzionano queste cose. – Donnie

+0

Penso che il vtable addizionale per le chiamate statiche dovrebbe fare il lavoro. Ad ogni modo, dipende solo da RTTI. PHP fa qualcosa del genere (anche se non esattamente). – doc

0

un approccio più che posso vedere qui è di fare una fabbrica di astratto: questo è C# non è necessario per fare un esempio di pollo di conoscere una serie di gambe. basta richiamare il metodo factory checken

abstract class AnimalFactory 
{ 
    public abstract Animal CreateAnimal(); 
    public abstract int GetLegs(); 

} 
abstract class Animal 
{ 

} 
internal class Chicken : Animal 
{ 

} 
class CreateChicken : AnimalFactory 
{ 
    public override Animal CreateAnimal() 
    { 
     return new Chicken(); 
    } 
    public override int GetLegs() 
    { 
     return 2; 
    } 

} 
0

Per un attimo, assumere "metodi statici astratti" sono ammessi.

Quindi, utilizzando il codice, aggiungo questo:

Animal modelAnimal; 
int numLegs; 

modelAnimal = this.getModelAnimal(4); // Got a dog 

numLegs = modelAnimal.getNumberOfLegs(); 

Prendo errore come modelAnimal che è un oggettocane cercherà di chiamare getNumberOfLegs in animali di classe e non Classe cane. Nessuna priorità per i metodi statici che conosci. Per evitare questa situazione, i progettisti non hanno incluso metodi statici astratti.