2008-09-29 6 views
42

Il existe des situations où il est pratique d'avoir une valeur de type null renvoyée par une distribution de type au lieu de lancer une exception ClassCastException. C# a l'opérateur as pour ce faire. Y at-il quelque chose d'équivalent disponible en Java, donc vous n'avez pas à vérifier explicitement l'exception ClassCastException?Comment émuler C# en tant qu'opérateur dans Java

Répondre

47

est ici une mise en œuvre comme, comme le suggère @Omar Kooheji:

public static <T> T as(Class<T> clazz, Object o){ 
    if(clazz.isInstance(o)){ 
     return clazz.cast(o); 
    } 
    return null; 
} 

as(A.class, new Object()) --> null 
as(B.class, new B()) --> B 
+1

Aaron, que se passe-t-il si je veux passer un type générique comme le paramètre clazz à votre fonction "as"? – mfalto

+1

self answer ... que je ne peux pas faire en Java à cause de l'effacement de type – mfalto

+2

J'aime la conception de la fonction ci-dessus 'as', mais cela me dérange le fait que la méthode' isInstance' sera invoquée deux fois, en raison de l'utilisation de la méthode 'cast' de la classe' Classe »En interne, cette méthode utilise la méthode 'IsIsntance' pour vérifier la compatibilité entre les types. Néanmoins, nous pouvons nous attendre à ce que la JVM puisse aligner le code 'cast' dans la fonction' as' donnée et après que le Jitter puisse supprimer les appels redondants à la méthode 'IsInstance', mais nous ne pouvons en être sûrs. –

13

Vous pouvez utiliser le mot-clé instanceof à la place du is de C#, mais il n'y a rien comme as.

Exemple:

if(myThing instanceof Foo) { 
    Foo myFoo = (Foo)myThing; //Never throws ClassCastException 
    ... 
} 
+9

Oui, le vieux double casting. Honte Java n'a pas quelque chose d'aussi élégant que l'opérateur 'as'. –

+1

'comme' est une chose très utile. –

25

Je pense que vous auriez à rouler votre propre:

return (x instanceof Foo) ? (Foo) x : null; 

EDIT: Si vous ne voulez pas que votre code client pour traiter les valeurs NULL, puis vous pouvez introduire un Null Object

interface Foo { 
    public void doBar(); 
} 
class NullFoo implements Foo { 
    public void doBar() {} // do nothing 
} 
class FooUtils { 
    public static Foo asFoo(Object o) { 
     return (o instanceof Foo) ? (Foo) o : new NullFoo(); 
    } 
} 
class Client { 
    public void process() { 
     Object o = ...; 
     Foo foo = FooUtils.asFoo(o); 
     foo.doBar(); // don't need to check for null in client 
    } 
} 
-2

je spécule, vous pouvez créas propably un opérateur comme

quelque chose comme

as<T,Type> (left, right) 
which evaluates to 
if (typeof(left) == right) 
    return (right)left 
else 
    return null 

Je ne sais pas comment vous le faites, je suis C# au moment et mon chapeau de Java a obtenu un peu poussiéreux depuis que je quitté l'université.

+1

Comparaison de type concret, c'est-à-dire 'typeof (left) == right' n'est pas ce que fait 'as'. – weston

10

Vous pouvez écrire une méthode d'utilitaire statique comme celle-ci. Je ne pense pas que ce soit terriblement lisible, mais c'est la meilleure approximation de ce que vous essayez de faire. Et si vous utilisez des importations statiques, ce ne serait pas trop mal en termes de lisibilité.

package com.stackoverflow.examples; 
public class Utils { 
    @SuppressWarnings("unchecked") 
    public static <T> T safeCast(Object obj, Class<T> type) { 
     if (type.isInstance(obj)) { 
      return (T) obj; 
     } 
     return null; 
    } 
} 

Voici un test qui montre comment cela fonctionne (et qu'il fonctionne.)

package com.stackoverflow.examples; 
import static com.stackoverflow.examples.Utils.safeCast; 
import static junit.framework.Assert.assertNotNull; 
import static junit.framework.Assert.assertNull; 

import org.junit.Test; 

public class UtilsTest { 

    @Test 
    public void happyPath() { 
     Object x = "abc"; 
     String y = safeCast(x, String.class); 
     assertNotNull(y); 
    } 

    @Test 
    public void castToSubclassShouldFail() { 
     Object x = new Object(); 
     String y = safeCast(x, String.class); 
     assertNull(y); 
    } 

    @Test 
    public void castToUnrelatedTypeShouldFail() { 
     Object x = "abc"; 
     Integer y = safeCast(x, Integer.class); 
     assertNull(y); 
    } 
} 
+0

Cela ne fonctionne pas réellement. isAssignableFrom teste si obj est une instance d'une super-classe de T. Ainsi, safeCast (new Object(), A.class) .doA() renverra une exception de cast de classe, plutôt qu'une NPE. –

+0

vous avez raison. J'ai eu les deux objets de classe à l'envers. J'ai édité la réponse et cela devrait être correct maintenant. –

+1

Y a-t-il une raison particulière pour utiliser type.isAssignableFrom (obj.getClass() au lieu de type.isInstance (obj)? – VoidPointer

0

En Java 8, vous pouvez également utiliser la syntaxe de flux avec facultatif:

Object o = new Integer(1); 

Optional.ofNullable(o) 
     .filter(Number.class::isInstance) 
     .map(Number.class::cast) 
     .ifPresent(n -> System.out.print("o is a number")); 
Questions connexes