Абстрактные статические методы в Scala - PullRequest
7 голосов
/ 21 марта 2012

Я читал этот соответствующий пост, но не было много конкретных ответов (плохой языковой дизайн более или менее): Почему статические методы не могут быть абстрактными в Java

Я немного новичок в Scala, возможно ли это (возможно, с чертами или чем-то еще)?

Я попытался, чтобы мой базовый класс расширил черту, но потом дочерние классы необходимы для реализации абстрактного статического метода в качестве метода-члена, когда я действительно хочу, чтобы их необходимо было реализовать в объекте-компаньоне.

Ответы [ 4 ]

5 голосов
/ 21 марта 2012

В Scala нет статических методов [*], поэтому ваш вопрос спорный.

Однако вы можете получить то, что хотите, расширив объект признаком:

scala> trait A { def foo(): Int }
defined trait A

scala> object C extends A { def foo(): Int = 5 }
defined module C

scala> C.foo
res0: Int = 5

который, вероятно, делает то, что вы хотите.На самом деле нет никакого способа заставить что-то быть реализовано в сопутствующем объекте класса.Сопутствующий объект может не существовать.

[*] Технически, есть, но это скорее деталь реализации, чем общая философия.См. Метод в сопутствующем объекте, скомпилированном в статические методы в Scala?

0 голосов
/ 03 мая 2018

Существует способ, который фактически используется всеми коллекциями Scala: определите черту Companion, содержащую определение абстрактного статического метода, который вы хотите реализовать, и добавьте абстрактный метод в базовую черту, которая должна возвращать этот компаньон.

Однако, чтобы заставить его работать, вы должны использовать хитрый аргумент типа:

// your trait - must give a ref to its companion
//   the type argument is made such that Self must extends MyTrait[Self]
//   and have as type argument of MyTrait the implementing subclass
trait MyTrait[Self <: MyTrait[Self]] { this: Self =>
  def companion: MyTraitCompanion[Self]
}

// the companion trait with required abstract static method
trait MyTraitCompanion[A <: MyTrait[A]] {
  def staticMethod: Int
}

// example of an implementing class of MyTrait, and MyTraitCompanion
case class SubClass(whatever: String) extends MyTrait[SubClass] {
  override def companion: MyTraitCompanion[SubClass] = SubClass
}

object SubClass extends MyTraitCompanion[SubClass] {
  override def staticMethod: Int = 42
}

// example of use
def print(t: MyTrait) = println("answer is " + t.companion.staticMethod)
print(SubClass("ultimate question"))

Как я уже говорил выше, это на самом деле используется коллекциями Scala: они должны реализовывать def companion: GenericCompanion[Self], который является абстрактным методом черты GenericTraversableTemplate. Хотя это сложнее, чем в моем примере.

0 голосов
/ 21 марта 2012

Я не уверен, что вы хотели бы сделать с абстрактным статическим методом в java, но единственный потенциальный вариант использования, который я видел, описан в прошлом (хотелось бы, чтобы я вспомнил, кем ...) вызывает метод непосредственно для параметра универсального типа.

т.е. если бы ява позволила что-то подобное ...

// this is not valid java...
// let's pretend we can define a static method in an interface
interface Foo {
    static String Foo();
}

// a class that implements our interface
class FooImpl implements Foo {
    public static String Foo() {return "foo";}  
}

... мы могли бы использовать его для универсального параметра, чтобы вызвать Foo () непосредственно для типа

static <T extends Foo> String Test() {
        return T.Foo(); // even if our hypothetical static interface
                        // was a valid thing, this would probably still fail
                        // due to type erasure      
}

Это будет иметь больше смысла в C #, потому что:

  1. Reified generics означает, что тип не стирается
  2. в C # операторы являются статическими методами, и вы можете определять свои собственные операторы

В основном это означает, что в «притворном C #» со статическими интерфейсами вы можете использовать что-то, соответствующее приведенному выше коду «притворяться в Java», для написания универсального метода, который работает с любым типом, который определяет конкретный оператор (например, со всем, что имеет оператор "+").

Теперь вернемся к скале. Как scala учитывает этот сценарий? Отчасти это не так. В Scala нет статических методов: объект - это одиночный объект (т. Е. Класс только с одним экземпляром), но он по-прежнему является классом с обычными методами экземпляра, т. Е. Для объектов, которые все еще вызывают методы только для одного экземпляра, а не непосредственно для типа ( четные операторы являются методами в scala).

Итак, в scala мы написали бы:

trait Foo { def Foo:String }
object FooImpl extends Foo { def Foo = "foo" }

def Test(f: Foo) = f.Foo

... и вызвать наш метод с

scala> Test(FooImpl)
res0: String = foo
// note that FooImpl is still an instance (the only one) of FooImpl and not 
// the type itself

Вы можете сделать некоторые трюки с последствиями, чтобы избежать передачи единственного экземпляра в качестве параметра:

implicit def aFoo = FooImpl
def Test2(implicit f: Foo) = f.Foo

теперь это работает:

scala> Test2
res1: String = foo

С более продвинутыми приемами с последствиями, scala также определяет Numeric, что позволяет вам использовать операторы для любого числового значения , даже если они не реализуют общий интерфейс из коробки.

0 голосов
/ 21 марта 2012

Есть два возможных объяснения того, почему абстрактные статические методы невозможны в Scala, Java, C ++ или C #.

Во-первых, это технический: абстрактные / виртуальные методы требуют ссылки на объект (называемый this ) для выбора переопределения, которое будет выполняться. Вы не предоставляете такой объект при вызове статического метода.

Второе логично: абстрактные / виртуальные статические методы не имеют никакого смысла. Когда вы вызываете статический метод, вы всегда знаете тип, который содержит этот метод. Вы пишете:

MyClass.DoSomething(args)

Если у вас есть MyDerivative, расширяющий MyClass, вы можете просто определить другой статический метод:

MyDerivative.DoSomethingDifferent(args)

В статических виртуальных методах просто нет смысла, поскольку они будут работать так же, как обычные статические методы.

...