Для Scala действительно ли возможно овеществить дженерики, не изменяя JVM?

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

function foo(x) {
  var tmp = 3;

  function bar(y) {
    console.log(x + y + (++tmp)); // will log 16
  }

  bar(10);
}

foo(2);

Это будет всегда регистрироваться 16, потому что bar может получить доступ x, который был определен как аргумент foo, и он может также получить доступ tmp от foo.

, Что закрытие. Функция не имеет к [1 138] возврат для вызова закрытия. Просто получающие доступ переменные за пределами Вашего непосредственного лексического контекста создает закрытие .

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + (++tmp)); // will also log 16
  }
}

var bar = foo(2); // bar is now a closure.
bar(10);

вышеупомянутая функция также зарегистрируется 16, потому что bar может все еще относиться к [1 110] и tmp, даже при том, что это больше не непосредственно в объеме.

Однако с тех пор tmp все еще бродит вокруг внутренний bar закрытие, оно также увеличивается. Это будет увеличено каждый раз, когда Вы звоните bar.

самый простой пример закрытия - это:

var a = 10;

function test() {
  console.log(a); // will output 10
  console.log(b); // will output 6
}
var b = 6;
test();

, Когда функция JavaScript вызывается, новый контекст выполнения создается. Вместе с аргументами функции и родительским объектом, этот контекст выполнения также получает все переменные, объявленные за пределами него (в вышеупомянутом примере, и и 'b').

возможно создать больше чем одну функцию закрытия, или путем возврата списка их или путем установки их на глобальные переменные. Все они будут относиться к тот же x и тот же tmp, они не делают свои собственные копии.

Здесь номер x является литеральным числом. Как с другими литералами в JavaScript, когда foo назван, номер x , скопировал в [1 120] как его аргумент x.

, С другой стороны, JavaScript всегда использует ссылки при контакте с объектами. Если говорят, Вы звонили foo с объектом, закрытие, которое он возвращает, будет ссылка тот исходный объект!

function foo(x) {
  var tmp = 3;

  return function (y) {
    console.log(x + y + tmp);
    x.memb = x.memb ? x.memb + 1 : 1;
    console.log(x.memb);
  }
}

var age = new Number(2);
var bar = foo(age); // bar is now a closure referencing age.
bar(10);

Как ожидалось, каждый вызов к [1 123] увеличит x.memb. То, что не могло бы ожидаться, то, что x просто называет тот же объект age переменная! После того, как несколько вызовов к [1 127], age.memb будут 2! Эта ссылка является основанием для утечек памяти с объектами HTML.

28
задан cdmckay 31 August 2009 в 19:51
поделиться

3 ответа

Нет, это не так. возможно, что Scala будет работать как Java-эквивалентный байт-код, если этот байт-код не поддерживает обобщенные универсальные шаблоны.

Когда вы спрашиваете «что нужно изменить?» , ответ будет: спецификация байт-кода . В настоящее время байт-код не позволяет определять параметризованный тип переменной. Было решено, что, поскольку модификация байт-кода для поддержки реифицируемых дженериков нарушит обратную совместимость , дженерики должны быть реализованы посредством стирания типа .

Чтобы получить вокруг этого, Scala использовала мощь своего неявного механизма для определения манифеста , который можно импортировать в любой области видимости для обнаружения информации о типах во время выполнения. Манифесты являются экспериментальными и в основном недокументированными, но они входят в состав библиотеки в версии 2.8 . Вот еще один хороший ресурс на обобщенных дженериках / манифестах Scala

21
ответ дан 28 November 2019 в 03:42
поделиться

В дополнение к oxbow_lakes, в Stack Overflow есть вопрос о , как обойти стирание типа в Scala .

5
ответ дан 28 November 2019 в 03:42
поделиться

«неявный манифест» - это трюк компилятора Scala, который не делает обобщения в Scala обобщенными. Компилятор Scala, когда он видит функцию с параметром «неявный m: Manifest [A]» и , он знает общий тип A на сайте вызова, он оборачивает класс A и его общий тип параметры в манифест и сделать его доступным внутри функции. Однако, если он не может определить истинный тип A, то у него нет возможности создать манифест. Другими словами, Manifest должен быть передан по цепочке вызова функции, если это необходимо внутренней функции.

scala> def typeName[A](a: A)(implicit m: reflect.Manifest[A]) = m.toString
typeName: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String

scala> typeName(List(1))
res6: java.lang.String = scala.collection.immutable.List[int]

scala> def foo[A](a: A) = typeName(a)
<console>:5: error: could not find implicit value for parameter m:scala.reflect.Manifest[A].
       def foo[A](a: A) = typeName(a)
                                  ^

scala> def foo[A](a: A)(implicit m: reflect.Manifest[A]) = typeName(a)
foo: [A](a: A)(implicit m: scala.reflect.Manifest[A])java.lang.String

scala> foo(Set("hello"))
res8: java.lang.String = scala.collection.immutable.Set[java.lang.String]
3
ответ дан 28 November 2019 в 03:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: