¿Puedo definir la interfaz Negatable en Java?

Zale:

Hacer esta pregunta para aclarar mi comprensión de las clases de tipos y tipos más altos kinded, no estoy en busca de soluciones en Java.


En Haskell, podría escribir algo así como

class Negatable t where
    negate :: t -> t

normalize :: (Negatable t) => t -> t
normalize x = negate (negate x)

Entonces suponiendo Booltiene una instancia de Negatable,

v :: Bool
v = normalize True

Y todo funciona bien.


En Java, no parece posible declarar una adecuada Negatableinterfaz. Podríamos escribir:

interface Negatable {
    Negatable negate();
}

Negatable normalize(Negatable a) {
    a.negate().negate();
}

Pero entonces, al contrario que en Haskell, lo siguiente sería que no se compile sin una conversión (se supone MyBooleanimplementos Negatable):

MyBoolean val = normalize(new MyBoolean()); // does not compile; val is a Negatable, not a MyBoolean

¿Hay una manera de referirse al tipo de aplicación en una interfaz Java, o se trata de una limitación fundamental del sistema de tipo Java? Si se trata de una limitación, se relaciona con soporte de tipo kinded de mayor? Creo que no: parece que este es otro tipo de limitación. Si es así, ¿tiene nombre?

Gracias, y por favor, hágamelo saber si la pregunta no es clara!

Jules:

En general, no.

Usted puede utilizar trucos (como se sugiere en las otras respuestas) que va a hacer este trabajo, pero que no proporcionan todas las mismas garantías que la clase de tipos de Haskell hace. En concreto, en Haskell, podría definir una función como esta:

doublyNegate :: Negatable t => t -> t
doublyNegate v = negate (negate v)

Ahora se sabe que el valor del argumento y el retorno del doublyNegateson a la vez t. Pero el equivalente de Java:

public <T extends Negatable<T>> T doublyNegate (Negatable<T> v)
{
    return v.negate().negate();
}

no lo hace, porque Negatable<T>podría ser implementado por otro tipo:

public class X implements Negatable<SomeNegatableClass> {
    public SomeNegatableClass negate () { return new SomeNegatableClass(); }
    public static void main (String[] args) { 
       new X().negate().negate();   // results in a SomeNegatableClass, not an X
}

Esto no es particularmente grave para esta aplicación, pero sí causa problemas para otras clases de tipos de Haskell, por ejemplo Equatable. No hay manera de implementar una aplicación Java Equatableclase de tipos sin necesidad de utilizar un objeto adicional y el envío de una instancia de ese objeto en torno a donde quiera que los valores que se comparan necesidad, (por ejemplo, enviar:

public interface Equatable<T> {
    boolean equal (T a, T b);
}
public class MyClass
{
    String str;

    public static class MyClassEquatable implements Equatable<MyClass> 
    { 
         public boolean equal (MyClass a, MyClass b) { 
             return a.str.equals(b.str);
         } 
    }
}
...
public <T> methodThatNeedsToEquateThings (T a, T b, Equatable<T> eq)
{
    if (eq.equal (a, b)) { System.out.println ("they're equal!"); }
}  

(De hecho, esto es exactamente cómo implementa Haskell tipo clases, pero oculta el paso de parámetros de usted por lo que no es necesario averiguar qué aplicación para enviar dónde)

Tratando de hacer esto con apenas aclara Java Interfaces conduce a unos resultados contrarios a la intuición:

public interface Equatable<T extends Equatable<T>>
{
    boolean equalTo (T other);
}
public MyClass implements Equatable<MyClass>
{
    String str;
    public boolean equalTo (MyClass other) 
    {
        return str.equals(other.str);
    }
}
public Another implements Equatable<MyClass>
{
    public boolean equalTo (MyClass other)
    {
        return true;
    }
}

....
MyClass a = ....;
Another b = ....;

if (b.equalTo(a))
    assertTrue (a.equalTo(b));
....

Era de esperar, debido al hecho de que equalToen realidad debería ser definido de forma simétrica, que si la ifdeclaración no compila, la afirmación también se compilará, pero no es así, ya que MyClassno es equiparable con Anotherpesar de que el revés es verdad . Pero con una Haskell Equatabletipo de clase, sabemos que si areEqual a bfunciona, entonces areEqual b atambién es válido. [1]

Otra limitación de interfaces frente a clases de tipo es que una clase tipo puede proporcionar un medio para crear un valor que implementa la clase de tipo sin tener un valor existente (por ejemplo, el returnoperador para Monad), mientras que para una interfaz ya debe tener un objeto del tipo con el fin de ser capaz de invocar sus métodos.

Usted pregunta si existe un nombre para esta limitación, pero no estoy al tanto de uno. Es simplemente porque las clases de tipos son realmente diferentes a las interfaces orientadas a objetos, a pesar de sus similitudes, ya que se aplican de esta manera fundamentalmente diferente: un objeto es un subtipo de su interfaz, por lo tanto lleva alrededor de una copia de los métodos de la interfaz directamente sin modificar su definición, mientras que una clase de tipo es una lista separada de las funciones de cada uno de los cuales se personaliza mediante la sustitución de las variables de tipo. No hay ninguna relación subtipo entre un tipo y una clase de tipo que tiene una instancia para el tipo (a Haskell Integerno es un subtipo de Comparable, por ejemplo: no simplemente existe unaComparable instancia que se puede pasar alrededor de unas necesidades cada vez de función para poder comparar sus parámetros y los parámetros resultan ser números enteros).

[1]: El Haskell ==operador se implementa en realidad el uso de un tipo de clase, Eq... no he utilizado esto porque la sobrecarga de operadores en Haskell puede ser confuso para las personas que no están familiarizados con la lectura de código de Haskell.

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=37880&siteId=1
Recomendado
Clasificación