2008-09-29 9 views
42

Existen situaciones en las que es práctico hacer que un valor de retorno de fundido de tipo sea nulo en lugar de arrojar una ClassCastException. C# tiene el operador as para hacer esto. ¿Hay algún equivalente disponible en Java para que no tenga que comprobar explícitamente ClassCastException?Cómo emular C# as-operator en Java

Respuesta

47

Aquí es una implementación de como, según lo sugerido por @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, ¿y si quiero pasar un tipo genérico como el parámetro clazz a su función "como"? – mfalto

+1

auto respuesta ... eso no se puede hacer en Java debido al borrado de tipo – mfalto

+2

Me gusta el diseño de la función 'como' anterior, pero me molesta el hecho de que el método' isInstance' se invocará dos veces, debido al uso del método 'cast' de la clase' Clase'.Internamente, este método utiliza el método 'IsIsntance' para verificar la compatibilidad entre los tipos. Sin embargo, podemos esperar que la JVM pueda alinear el código 'moldeado 'dentro de la función' as' dada y luego el Jitter puede eliminar llamadas redundantes al método 'IsInstance', pero no podemos estar seguros de eso. –

13

Puede usar la palabra clave instanceof en lugar de C# 's is, pero no hay nada como as.

Ejemplo:

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

Sí, el viejo reparto doble. La vergüenza de Java no tiene algo tan elegante como el operador 'as'. –

+1

'como' es una cosa muy útil. –

25

yo creo que tendría que rodar su propia:

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

EDIT: Si no desea que su código de cliente para hacer frente a los nulos, entonces se puede introducir 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

estoy especulando que además probablemente podría Creas un operador como

algo así como

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

No estoy seguro de cómo se haría, yo soy una C# en el momento y mi sombrero de Java ha conseguido un poco de polvo desde que salí de la universidad.

+1

Comparación del tipo de hormigón, es decir 'typeof (izquierda) == right' no es lo que' as' hace. – weston

10

Puede escribir un método de utilidad estático como este. No creo que sea terriblemente legible, pero es la mejor aproximación de lo que estás tratando de hacer. Y si usa importaciones estáticas, no sería tan malo en términos de legibilidad.

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; 
    } 
} 

Aquí es un caso de prueba que muestra cómo funciona (y que funciona.)

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

Esto realmente no funciona. isAssignableFrom pruebas para ver si obj es una instancia de una superclase de T. Por lo tanto, safeCast (new Object(), A.class) .doA() devolverá una excepción de clase de lanzamiento, en lugar de un NPE. –

+0

tienes razón. Tenía los dos objetos de clase hacia atrás. He editado la respuesta y debería ser correcta ahora. –

+1

¿Hay alguna razón en particular para utilizar type.isAssignableFrom (obj.getClass() en lugar de type.isInstance (obj)? – VoidPointer

0

En java 8 también puede usar la sintaxis de flujo con Opcional:

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"));