简体   繁体   English

Java泛型推断类型

[英]Java Generics Inferred Types

Ok, so I am implementing the state monad in java. 好的,所以我在java中实现状态monad。 However, I can't seem to get the generics to work right. 但是,我似乎无法让泛型工作正常。 I have the code below, and am trying to avoid the cases indicated. 我有下面的代码,我正在努力避免指出的情况。

public interface Monad<M, A> 
{
    <B, R extends Monad<M, B>> R bind(Function<? super A, R> p_function);
}

public class State<S, A> implements Monad<State<S, ?>, A>
{
    private Function<S, Pair<S, A>> m_function;

    public State(Function<S, Pair<S, A>> p_function)
    {
        m_function = p_function;
    }

    public final Pair<S, A> run(S p_state)
    {
        return m_function.apply(p_state);
    }

    @Override
    public <B, R extends Monad<State<S, ?>, B>> R bind(
            final Function<? super A, R> p_function) 
    {
        // I want to avoid the cast to R here
        return (R) new State<S, B>((S state) -> {
            Pair<S, A> run = run(state);
            // And this cast, but they seem related
            State<S, B> applied = (State<S, B>) p_function.apply(run.second());
            return applied.run(run.first());
        });
    }
}


Note: I am aware that if I cange the signature of bind to 注意:我知道如果我能够bind的签名
<B> Monad<M, B> bind(Function<? super A, ? extends Monad<M, B>> p_function);
The cast can be avoided. 演员阵容可以避免。 However, this causes a compile error in the following method 但是,这会导致以下方法中的编译错误

public static <A, B, C, M, MB extends Monad<M, B>, MC extends Monad<M, C>> 
Function<A, MC> compose(
        Function<? super A, MB> p_first, Function<? super B, MC> p_second)
{
    // have to use an anonymous class here, because using a closure causes a
    // runtime error with the beta version of JDK 8
    return new Function<A, MC>() {

        @Override
        public MC apply(A arg) {
            MB monadOfB = p_first.apply(arg);
            return monadOfB.<C> bind(p_second); // <-- type error here
        }
    };
}


Now, I also tried changing the signature of compose in a similar manner. 现在,我也尝试以类似的方式改变compose的签名。 ie rather than MB extends Monad<M, B> I used Monad<M, B> where MB was used and similarly for MC. 即,而不是MB extends Monad<M, B>我使用Monad<M, B>其中使用MB并且类似地使用MC。 This makes the compose method compile. 这使得compose方法编译。 However, then the return type could not be correctly inferred by the callers of compose ie 然而, compose的调用者无法正确推断返回类型

Function<String, State<Integer, String>> left = ...; 
Function<String, State<Integer, String>> right = ...; 
Function<String, State<Integer, String>> composed = Monad.compose(left, right);

Doesn't work without specifying the types on the method call, whereas before it did. 如果没有在方法调用上指定类型,则不起作用,而在此之前。

How do I make all these generics play nicely together? 如何使所有这些仿制药一起很好地发挥作用?

For your example to work, you need your classes to be defined similarly to: 为了使您的示例正常工作,您需要以类似于以下方式定义类:

class State<S, B> extends Monad<State<S, ?>, B> {}
class Monad<T, U> {}

R is a subclass of Monad<State<S, ?>, B> , and sb is a subclass of Monad<State<S, ?>, B> too, but there is no reason that it also is a R . RMonad<State<S, ?>, B>的子类,sb也是Monad<State<S, ?>, B>的子类,但没有理由它也是R

It is like writing: 这就像写作:

Number n = 123.5d;
Integer i = n; //does not compile: cast required
Integer j = (Integer) n; //throws an exception

EDIT 编辑

I'm not familiar with what you are trying to achieve, and this simplication might not achieve your aim, but it would compile (I have removed the lambdas as I don't have a jdk8 compiler installed at the moment): 我不熟悉你想要实现的目标,这个简化可能无法实现你的目标,但它会编译(我已经删除了lambdas,因为我目前没有安装jdk8编译器):

public class Test1 {

    public static <A, B, C, M> Function<A, Monad<M, C>> compose(final Function<? super A, Monad<M, B>> p_first, 
                                                                final Function<? super B, Monad<M, C>> p_second) {
        // have to use an anonymous class here, because using a closure causes a runtime error
        // with the beta version of JDK 8
        return new Function<A, Monad<M, C>>() {
            @Override
            public Monad<M, C> apply(A arg) {
                Monad<M, B> monadOfB = p_first.apply(arg);
                return monadOfB.bind(p_second); // <-- type error here
            }
        };
    }
}

interface Monad<M, A> {

    <B> Monad<M, B> bind(Function<? super A, Monad<M, B>> p_function);
}

class State<S, A> implements Monad<State<S, ?>, A> {

    private Function<S, Pair<S, A>> m_function;

    public State(Function<S, Pair<S, A>> p_function) {
        m_function = p_function;
    }

    public final Pair<S, A> run(S p_state) {
        return m_function.apply(p_state);
    }

    @Override
    public <B> Monad<State<S, ?>, B> bind(final Function<? super A, Monad<State<S, ?>, B>> p_function) {
        // I want to avoid the cast to R here
        return new State<S, B>(new Function<S, Pair<S, B>>() {
            public Pair<S, B> apply(S state) {
                Pair<S, A> run = run(state);
                // And this cast, but they seem related
                State<S, B> applied = (State<S, B>) p_function.apply(run.second());
                return applied.run(run.first());
            }
        });
    }
}

Here's a version that passes the Java 7 type checker: 这是一个通过Java 7类型检查器的版本:

class State<S, A> implements Monad<State<S, ?>, A> {
    private Function<S, Pair<S, A>> m_function;

    public State(Function<S, Pair<S, A>> p_function)
    {
        m_function = p_function;
    }

    public final Pair<S, A> run(S p_state)
    {
        return m_function.apply(p_state);
    }

    @Override
    public <B> Monad<State<S, ?>, B> bind(
            final Function<? super A, ? extends Monad<State<S, ?>, B>> p_function) {
        return new State<S, B>(
                new Function<S, Pair<S,B>>() {
                    public Pair<S,B> apply(S state) {
                        Pair<S, A> run = run(state);
                        // And this cast, but they seem related
                        State<S, B> applied = (State<S, B>) p_function.apply(run.second());
                        return applied.run(run.first());
                    }
        });
    }

    public static <A, B, C, M> 
    Function<A, Monad<M,C>> compose(
            final Function<? super A, ? extends Monad<M,B>> p_first, 
            final Function<? super B, ? extends Monad<M,C>> p_second) {
        return new Function<A, Monad<M,C>>() {
            @Override
            public Monad<M,C> apply(A arg) {
                Monad<M,B> monadOfB = p_first.apply(arg);
                return monadOfB.<C>bind(p_second);
            }
        };
    }
}

The problem with your original code was that you wanted to pick a particular instantiation for R but your signature allowed the caller to pick it. 您的原始代码的问题是您想为R选择一个特定的实例,但您的签名允许调用者选择它。 The signature <B, R extends Monad<State<S, ?>, B>> R bind(final Function<? super A, R> p_function) lets the caller choose an arbitrary subtype of Monad<State<S, ?>,B> and demand that bind return it. 签名<B, R extends Monad<State<S, ?>, B>> R bind(final Function<? super A, R> p_function)让调用者选择Monad<State<S, ?>,B>的任意子类型Monad<State<S, ?>,B>并要求bind返回它。 Your bind implementation didn't do that, though: it decided to always return a particular subtype of R of your choosing. 但是你的bind实现没有这样做:它决定总是返回你选择的R的特定子类型。 So your signature was promising one thing, but your implementation was doing another. 所以你的签名是有前途的,但是你的实现正在做另一件事。

My fix removes that promise. 我的修复删除了这个承诺。 Instead of allowing the caller to choose a particular Monad<State<S,?>,B> subtype, the revised signature just promises to return some subtype -- which in this case means I can dispense with the generic variable entirely and just substitute the upper bound type directly in the signature. 修改后的签名只允许返回一些子类型 - 在这种情况下意味着我可以完全免除泛型变量并且只需替换它,而不是允许调用者选择特定的Monad<State<S,?>,B>子类型。直接在签名中的上限类型。

A similar problem affected the compose method -- allowing the caller to choose MB and MC types, but then expecting to be able to produce your own choice of subtypes for MB and MC in the implementation. 类似的问题影响了compose方法 - 允许调用者选择MBMC类型,但是期望能够在实现中为MBMC生成您自己选择的子类型。 I fixed that in a similar way. 我以类似的方式解决了这个问题。

(I'm +1'ing jacobm's answer, I just wanted to elaborate a bit on the underlying problem.) (我是jacobm的答案,我只想详细说明根本问题。)

The problem is that in Java, there is no particular relationship between GenericClass<S> and GenericClass<T> : I mean, both are subtypes of GenericType<?> , but there's no way that GenericInterface<T> can refer to the type you'd get by taking getClass() and substituting T for S . 问题是在Java中, GenericClass<S>GenericClass<T>之间没有特定的关系:我的意思是,两者都是GenericType<?>子类型,但是GenericInterface<T>没有办法可以引用你的类型通过取getClass()并用T代替S

In Haskell, the definition of the Monad typeclass looks like this: 在Haskell中, Monad类型类的定义如下所示:

class Monad m where
    (>>=)  :: m a -> (a -> m b) -> m b
    return :: a -> m a

Note that it defines m by using both ma and mb , meaning "the same parameterized type m , with (potentially) different type-arguments a and b ". 注意,它通过使用mamb来定义m ,意思是“相同的参数化类型m ,具有(可能)不同的类型参数ab ”。 In Java you can't create a supertype of m (that is, an interface for m ) that expresses this sort of thing, because while the supertype can refer to itself with arbitrary type-parameters (because it can refer to itself by name, just like how it could use any other generic type), and can refer to an arbitrary subtype with any single type-argument (namely its own), it has no way to refer to an arbitrary subtype with an arbitrary type-parameter. 在Java中,你不能创建的超类型m (即一个接口m )表达这样的事情,因为虽然超可以参考自己任意类型的参数(因为它可以通过名称来指代自己,就像它可以使用任何其他泛型类型一样,并且可以引用具有任何单个类型参数的任意子类型(即它自己的),它无法使用任意类型参数引用任意子类型。 It doesn't sit "outside" the type system in the way that a Haskell typeclass definition does. 它不像Haskell类型类定义那样位于类型系统的“外部”。

This means that there's no real way to define a generic Monad interface whose implementations are generic monadic types. 这意味着没有真正的方法来定义通用Monad接口,其实现是通用的monadic类型。

Tell me if this simplification is still representative of your question: 告诉我,如果这种简化仍然代表您的问题:

The following compiles: 以下编译:

public class TestClass {
    public interface Monad {
        <R extends Monad> R bind();
    }

    public class State implements Monad {
        @Override
        public <R extends Monad> R bind() {
            return (R) new State(); // [1]
        }
    }

    public <M extends Monad> M apply() {
        M subMonad = null;
        return subMonad.bind();
    }
}

You want to remove the cast from [1]. 您想从[1]中删除演员表。 If you do it this way: 如果你这样做:

public class TestClass {
    public interface Monad {
        Monad bind();
    }

    public class State implements Monad {
        @Override
        public Monad bind() {
            return new State();
        }
    }

    public <M extends Monad> M apply() {
        M subMonad = null;
        return subMonad.bind(); // [2]
    }
}

Then [2] doesn't compile. 然后[2]不编译。 Does this sum it up? 这总结了吗? If so, can the apply() method simply return a Monad ? 如果是这样, apply()方法可以简单地返回一个Monad吗?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM