且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何定义返回类型基于 Scala 中的参数类型和类型参数的方法?

更新时间:2023-11-26 18:08:46

Typeclasses 可以帮助您解决问题.首先让我们定义简单的特征:

Typeclasses can help you to solve the problem. First let's define simple trait:

trait Composeable[A, B, R] {
    def compose(a: A, b: B): R
}

它只需要 ab 并将它们组合在其他一些 R 类型的容器中.

It just takes a and b and composes them in some other container of type R.

现在让我们为伴随对象中的 CompleteBindingIncompleteBinding 定义 2 个具体的实现,并使它们隐式:

Now let's define 2 concrete implementations of it for CompleteBinding and IncompleteBinding in companion object and also make them implicit:

object Composeable extends LowPriorityComposables {
    implicit def  comCommplete[T] = new Composeable[ObservableValue[T], BindableValue[T], CompleteBinding[T]] {
        def compose(a: ObservableValue[T], b: BindableValue[T]) = new CompleteBinding(a, b)
    }
}

trait LowPriorityComposables {
    implicit def  comIncommplete[S, T] = new Composeable[ObservableValue[S], BindableValue[T], IncompleteBinding[S, T]] {
        def compose(a: ObservableValue[S], b: BindableValue[T]) = new IncompleteBinding(a, b)
    }
}

如您所见,实现非常简单.它只需要 ObservableValueBindableValue 然后在 Binding 中组合.我将它们分成不同的特征,因为通常它们都可以使用,如果您观察并绑定某种类型 T 的相同值(CompleteBinding 的情况).通过在单独的 trait 中提取 comIncommplete,我告诉编译器,如果没有找到其他合适的隐式,它应该使用它.换句话说,您告诉编译器始终尝试应用 CompleteBinding,如果无法完成,则应应用 IncompleteBinding.

As you can see, implementation is pretty straightforward. It just takes ObservableValue and BindableValue and combines then in Binding. I separated them in different traits because generally they both can be used, if you observe and bind the same value of some type T (the case of CompleteBinding). By extracting comIncommplete in separate trait I told compiler, that it should use it in case if no other suitable implicit was found. In other word you are telling compiler to always try to apply CompleteBinding and if it cannot be done, then IncompleteBinding should be applied.

剩下的就是定义使用 Composeable 类型类的 bindTo 方法:

The only thing remains is to define bindTo method that uses Composeable type class:

def bindTo[S, R](o: ObservableValue[S])(implicit ev: Composeable[ObservableValue[S], BindableValue[T], R]): R = 
    ev.compose(o, this)

这里我说,第一个参数是 o,它是一些 ObservableValue 包含 S 类型的值,但我也想编译找到一个证据,证明存在一些隐含的,证明我可以用 BindableValue[T] 组合 ObservableValue[S].当我有这样的证据时,我只是用这个(可绑定的)组成可观察的.如您所见,类型参数 R 类似于***变量 - 编译器可以自己解决它,因此您始终可以从 bindTo 方法返回 concreate 类型信息.

Here I say, that the first argument is o which is some ObservableValue that contains values of type S, but I also want compile to find an evidence, that there exist some implicit, that proves, that I can compose ObservableValue[S] with BindableValue[T]. When i have such evidence, I just compose observable with this (bindable). As you can see, type parameter R is something like free variable - compiler can figure it out but itself, so you always have concreate type information returned from the bindTo method.

现在你所有的测试用例都应该可以正常编译了:

Now all you test cases should compile just fine:

val a: CompleteBinding[Int] = bindable1 bindTo property1
val b: IncompleteBinding[String, Int] = bindable1 bindTo property2
val c: CompleteBinding[Option[Int]] = bindable2 bindTo event1
val d: IncompleteBinding[Option[String], Option[Int]] = bindable2 bindTo event2