Scala无法推断Java方法的类型参数

时间:2017-09-10 14:07:29

标签: scala types

我在Java中有以下复杂的类型层次结构:

Build.VERSION.SDK_INT < Build.VERSION_CODES.M

我有一些处理player.setDataSource(this, mediaUri) s的静态方法:

// the first type
interface Element<Type extends Element<Type>> {
    Type foo(Type a, Type b);
}

// the second type
interface Payload<Type extends Payload<Type>> {
    Type bar(Type[] array);
}

// some toy implementation
final class SomePayload implements Payload<SomePayload> {
    @Override
    public SomePayload bar(SomePayload[] array) { return array[0]; }
}

// mix of first and second interfaces
interface ComplicatedElement<
              PayloadT extends Payload<PayloadT>,
              ObjectT extends ComplicatedElement<PayloadT, ObjectT>>
          extends Element<ObjectT> {

    PayloadT getPayload();

    ObjectT add(ObjectT a, ObjectT b);
}

// some toy implementation
final class SomeComplicatedElement 
      implements ComplicatedElement<SomePayload, SomeComplicatedElement> {
    final SomePayload data;

    public SomeComplicatedElement(SomePayload data) {
        this.data = data;
    }

    @Override
    public SomePayload getPayload(){ return data; }

    @Override
    public SomeComplicatedElement foo(SomeComplicatedElement a, SomeComplicatedElement b) {
        return b;
    }

    @Override
    public SomeComplicatedElement add(SomeComplicatedElement a, SomeComplicatedElement b) {
        return a;
    }
}

现在,从Java我可以调用ComplicatedElement而不会出现这样的问题:

public static <PayloadT extends Payload<PayloadT>,
              ObjectT extends ComplicatedElement<PayloadT, ObjectT>>
List<ObjectT> method(ObjectT input) {
    return Collections.singletonList(input);
}

然而,当我尝试在Scala中做同样的事情时:

method

我有这个编译错误:

public static void main(String[] args) { System.out.println(method(new SomeComplicatedElement(new SomePayload()))); }

我可以通过明确指定类型参数来解决这个问题:

import FooBarJava.{SomeComplicatedElement, SomePayload, method}

def main(args: Array[String]): Unit = {
  println(method(new SomeComplicatedElement(new SomePayload())))
}

但它非常烦人,我想避免这种情况(我想这可能是因为Java编译器可以正常工作)。有没有办法这样做?

2 个答案:

答案 0 :(得分:3)

  

(我想这可能是因为Java编译器可以正常工作)

Scala具有比Java更丰富的类型系统。例如,在Java中,Scala中没有类似于Nothing的类型(即通用子类型)。因此,有时类型系统语言中的编译器无法推断类型,而类似情况下,使用较差类型系统的语言编译器可能会这样做。

如果指定类型参数太烦人,为什么不创建一些辅助方法?

private def methodWithPayload(data: SomePayload): java.util.List[SomeComplicatedElement] =
    method[SomePayload, SomeComplicatedElement](new SomeComplicatedElement(data))

methodWithPayload(new SomePayload)

在Java中,唯一的选项是<SomePayload, SomeComplicatedElement>method(..),在Scala中有两个选项method[SomePayload, SomeComplicatedElement](..)method[Nothing, SomeComplicatedElement](..),两个选项都有效。

答案 1 :(得分:0)

我提出的一个丑陋的黑客(在使用第三方Java库时特别有用)是在Scala项目中创建一个单独的Java文件,它会使所有必需的不安全类型擦除:

import FooBarJava.{SomeComplicatedElement, SomePayload}
import HackScala.method

def main(args: Array[String]): Unit = {
  // this will work fine and return correct type
  println(method(new SomeComplicatedElement(new SomePayload())))
}

然后所有人都可以在Scala中正常工作:

public class ObservableDictonary<TKey, TValue> : Dictionary<TKey, TValue>, INotifyCollectionChanged, INotifyPropertyChanged
{
    public event NotifyCollectionChangedEventHandler CollectionChanged;

    public event PropertyChangedEventHandler PropertyChanged;

    public new void Add(TKey key, TValue value)
    {
        base.Add(key, value);
        if (!TryGetValue(key, out _)) return;
        var index = Keys.Count;
        OnPropertyChanged(nameof(Count));
        OnPropertyChanged(nameof(Values));
        OnCollectionChanged(NotifyCollectionChangedAction.Add, value, index);
    }

    public new void Remove(TKey key)
    {
        if (!TryGetValue(key, out var value)) return;
        var index = IndexOf(Keys, key);
        OnPropertyChanged(nameof(Count));
        OnPropertyChanged(nameof(Values));
        OnCollectionChanged(NotifyCollectionChangedAction.Remove, value, index);
        base.Remove(key);
    }

    public new void Clear()
    {

    }

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
    {
        PropertyChanged?.Invoke(this, e);
    }

    protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        CollectionChanged?.Invoke(this, e);
    }

    private void OnPropertyChanged(string propertyName)
    {
        OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }

    private void OnCollectionChanged(NotifyCollectionChangedAction action, object item)
    {
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item));
    }

    private void OnCollectionChanged(NotifyCollectionChangedAction action, object item, int index)
    {
        OnCollectionChanged(new NotifyCollectionChangedEventArgs(action, item, index));
    }

    private int IndexOf(KeyCollection keys, TKey key)
    {
        var index = 0;
        foreach (var k in keys)
        {
            if (Equals(k, key))
                return index;
            index++;
        }
        return -1;
    }
}