2013-07-02 12 views
7

Java ha marcatori speciali su metodi chiamati synthetic e bridge.È possibile utilizzare un metodo sintetico o bridge per uniformare una modifica int -> double API?

JLS 13.1.7, "Qualsiasi costrutti introdotti da un compilatore Java che non hanno un corrispondente costrutto nel codice sorgente devono essere contrassegnati come sintetico ..."

metodi Così sintetici sono nulla generato dal compilatore e non rappresentato nel codice sorgente e benché non sia menzionato in quella specifica PDF molto bene, i metodi bridge vengono utilizzati per tipical generici. (Ad esempio Animal.interactWith(Creature c) ottiene un metodo di bridge interactWith(Object c), che getta al Creatura e chiama l'altro metodo.)


Abbiamo questo API chiamata Bukkit, che fornisce l'accesso stabile a cambiare il funzionamento di un server di Minecraft. Un aspetto della sottostante implementazione dell'API (aka vanilla Minecraft), di cui abbiamo poco controllo, è stato recentemente forzato nella versione 1.6.1 per passare dai valori interi ai valori float. E nell'interesse di evitare la difficoltà di un altro cambiamento, abbiamo scelto di modificare tutti i nostri metodi API su double s.

Così, ad esempio:

public int getHealth(); 
public void setHealth(int health); 
// Must now be 
public double getHealth(); 
public void setHealth(double health); 

Tuttavia, come sempre, vorremmo plugin compilati con la versione precedente, 1.5.2, a lavorare ancora per quanto possibile - questo è il punto di tutta l'API.

Il setHealth è un problema risolto, basta introdurre un sovraccarico. Al momento, abbiamo un metodo denominato _INVALID_getHealth(V)I che viene rinominato in fase di compilazione dell'implementazione (non compilazione API) su getHealth(V)I e questo consente ai vecchi plug-in di continuare a funzionare.

Tuttavia, quando qualcuno tenta di estendere la nostra implementazione di questi metodi rinominati, ottengono errori di compilazione dai metodi con doppio nome e sovrascrittura.

C'è un modo per fornire sia le int e double rendimenti utilizzando un manualmente/ metodo sintetico o ponte utensile inserito, tale da non causare errori di compilazione per coloro che cercano di cambiare le parti della nostra implementazione API ?

+1

Perché non denominare il metodo qualcosa di diverso e solo deprecare l'originale (o addirittura lasciarlo non deprecato), e farlo semplicemente restituire il doppio valore arrotondato a int? –

+0

@ increment1 Sono d'accordo con te su come dovresti solo cambiare il nome ... Non riesco a ricordare quale oggetto Bukkit fosse, ma ricordo di aver visto metodi come '_INVALID_setHealth (int i)' e 'setHealth (double d)' – Jojodmo

+0

@Jojodmo Suppongo che dovrei scrivere una risposta ... (Affermativo, sintetico + bridge è ciò di cui hai bisogno per compilare una sottoclasse) – Riking

risposta

2

La risposta è sì - se si contrassegna un metodo con diverso tipo di ritorno, ma gli stessi parametri sia sintetico e il ponte, è possibile compilare sottoclassi.

In questo caso specifico, è stata creata una nuova utility Maven, "Overmapper", per eseguire questa attività, sebbene sia possibile eseguirla in qualsiasi strumento automatizzato in grado di modificare il bytecode Java.Ecco il file di configurazione:

members: 
    "org/bukkit/entity/Damageable _INVALID_damage (I)V": damage 
    "org/bukkit/entity/Damageable _INVALID_damage (ILorg/bukkit/entity/Entity;)V": damage 
    "org/bukkit/entity/Damageable _INVALID_getHealth()I": getHealth 
    "org/bukkit/entity/Damageable _INVALID_setHealth (I)V": setHealth 
    "org/bukkit/entity/Damageable _INVALID_getMaxHealth()I": getMaxHealth 
    "org/bukkit/entity/Damageable _INVALID_setMaxHealth (I)V": setMaxHealth 
    "org/bukkit/entity/LivingEntity _INVALID_getLastDamage()I": getLastDamage 
    "org/bukkit/entity/LivingEntity _INVALID_setLastDamage (I)V": setLastDamage 
    "org/bukkit/event/entity/EntityDamageEvent _INVALID_getDamage()I": getDamage 
    "org/bukkit/event/entity/EntityDamageEvent _INVALID_setDamage (I)V": setDamage 
    "org/bukkit/event/vehicle/VehicleDamageEvent _INVALID_getDamage()I": getDamage 
    "org/bukkit/event/vehicle/VehicleDamageEvent _INVALID_setDamage (I)V": setDamage 
    "org/bukkit/event/entity/EntityRegainHealthEvent _INVALID_getAmount()I": getAmount 
    "org/bukkit/event/entity/EntityRegainHealthEvent _INVALID_setAmount (I)V": setAmount 
    "org/bukkit/entity/Minecart _INVALID_getDamage()I": getDamage 
    "org/bukkit/entity/Minecart _INVALID_setDamage (I)V": setDamage 
    "org/bukkit/entity/Projectile _INVALID_getShooter()Lorg/bukkit/entity/LivingEntity;": getShooter 
    "org/bukkit/entity/Projectile _INVALID_setShooter (Lorg/bukkit/entity/LivingEntity;)V": setShooter 
    "org/bukkit/Bukkit _INVALID_getOnlinePlayers()[Lorg/bukkit/entity/Player;": getOnlinePlayers 
    "org/bukkit/Server _INVALID_getOnlinePlayers()[Lorg/bukkit/entity/Player;": getOnlinePlayers 
flags: 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftLivingEntity getHealth()I": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftEnderDragonPart getHealth()I": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftLivingEntity getMaxHealth()I": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftEnderDragonPart getMaxHealth()I": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftLivingEntity setHealth (I)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftEnderDragonPart setHealth (I)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftLivingEntity setMaxHealth (I)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftEnderDragonPart setMaxHealth (I)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftLivingEntity damage (I)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftEnderDragonPart damage (I)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftLivingEntity damage (ILorg/bukkit/entity/Entity;)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftEnderDragonPart damage (ILorg/bukkit/entity/Entity;)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftLivingEntity getLastDamage()I": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftLivingEntity setLastDamage (I)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftMinecart setDamage (I)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftMinecart getDamage()I": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftProjectile getShooter()Lorg/bukkit/entity/LivingEntity;": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftProjectile setShooter (Lorg/bukkit/entity/LivingEntity;)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftArrow getShooter()Lorg/bukkit/entity/LivingEntity;": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftArrow setShooter (Lorg/bukkit/entity/LivingEntity;)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftFireball getShooter()Lorg/bukkit/entity/LivingEntity;": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftFireball setShooter (Lorg/bukkit/entity/LivingEntity;)V": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftFish getShooter()Lorg/bukkit/entity/LivingEntity;": 0x1001 
    "org/bukkit/craftbukkit/v${minecraft_version}/entity/CraftFish setShooter (Lorg/bukkit/entity/LivingEntity;)V": 0x1001 

Si può vedere che tutti i metodi int-ritorno vengono modificate per essere contrassegnato come ponte e sintetico.

-1

Se si desidera fare qualcosa con una salute delle entità è possibile eseguire il cast in un Damageable e quindi non ci sarà più un errore di compilazione.

Entity e = /* some entity */; 
double health = ((Damageable) e).getHealth(); 

Questo dovrebbe funzionare. spero che aiutato

+2

Questa domanda proviene da un punto di vista della creazione dell'API, non da un punto di vista dell'utilizzo dell'API. Inoltre, la soluzione plugin-developer è di mettere bukkit.jar più in alto nell'elenco delle dipendenze. – Riking