Аргументы конструктора видны во всем классе - функция или ошибка? - PullRequest
1 голос
/ 05 декабря 2011

Рассмотрим такой код (это просто пример не настоящий код):

class Foo(url : String) extends Bar(url)
{
  def say() { println(url) }
}

Он компилируется и работает.С глупыми результатами "конечно".Я слишком новичок, чтобы судить, но для меня это не имеет смысла, кроме путаницы - по определению невозможно, чтобы аргумент конструктора был доступен непосредственно другим способом.

Может ли кто-то иметь больше опыта в Scala?указать условие, когда оно может работать или иметь смысл?Или подтвердите мое подозрение, что это недостаток в текущем компиляторе Scala.

Обновление

class Bar(val Url : String)
{
}

Почему "ерунда".Потому что моя воля (без «var» и «val» в Foo) состояла в том, чтобы просто передать аргумент, ничего больше.Поэтому, когда я на самом деле использую аргумент конструктора, это просто вопрос того, какую сущность я использую по ошибке .Когда вы пишете специально, вы каждый раз выигрываете джекпот, но если вы этого не сделаете (то есть допустили ошибку в написании), вы используете объекты случайным образом.Это не код не имеет смысла, это вычисление просто не имеет смысла, я мог бы также бросить кости.Существует область действия метода, и я не вижу причины, по которой не должно быть области видимости конструктора.

Обновление - обходной путь

Так что, похоже, эта злая конструкция действительночасть языка (по замыслу).Как предположили UserUnknown и Tomasz Nurkiewicz, единственной линией защиты от глупой опечатки является условность, однако нижний регистр не годится.«А» и «а» сильно различаются, но «U» и «и» не сильно.Введение одного специального символа (как показал Томаш) намного лучше, потому что можно визуально обнаружить подозрительное использование аргумента конструктора.

Я буду использовать «$» для «только что передаваемых» аргументов конструктора, это сложнеевведите для меня, и вы не видите этот символ слишком часто в коде.

Спасибо за ответы!

Почему это зло?Поскольку неявные действия должны быть разрешены явно пользователями - хорошие примеры являются «динамическими» в C #, неявные преобразования в Scala.И примеры нарушения этого правила, которое привело к множеству проблем: неявные преобразования в bool в C ++, неявные конструкторы в C ++, объявление с использованием в Perl.И этот конкретный случай очень, очень близок к упомянутому перлизму, в Perl наконец-то произошли изменения в интерпретаторе, чтобы обнаружить такие злоупотребления, почему Scala повторил ту же ошибку?Интересно.

Ответы [ 3 ]

12 голосов
/ 05 декабря 2011

Ваши подозрения совершенно беспочвенны. Это по замыслу.

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

Так что, по сути, если вам не нужно это как поле, это не будет. Если вы это сделаете, так и будет. И вы никогда не напишете ни одного лишнего символа кода, чтобы сообщить компилятору, что он может выяснить сам.

7 голосов
/ 05 декабря 2011

Это не ошибка, это особенность. На самом деле, действительно хороший. Нужен пример, насколько это полезно? Вот как я использую его с Spring и внедрением зависимостей через конструктор:

@Service
class Foo @Autowired() (bar: Bar, jdbcOperations: JdbcOperations) {

    def serverTime() = bar.format(jdbcOperations.queryForObject("SELECT now()", classOf[Date]))

}

Эквивалентный код в Java:

@Service
public class Foo
{
    private final Bar bar;
    private final JdbcOperations jdbcOperations;

    @Autowired
    public Foo(Bar bar, JdbcOperations jdbcOperations)
    {
        this.bar = bar;
        this.jdbcOperations = jdbcOperations;
    }

    public String serverTime()
    {
        return this.bar.format(this.jdbcOperations.queryForObject("SELECT now()", Date.class));
    }

}

Все еще не убеждены?

Краткое руководство:

class Foo(var x: Int, val y: Int, z: Int) {
  println(z)
  //def zz = z
}

x станет var применимым с геттерами и сеттерами . y станет неизменной переменной, а z станет неизменной переменной, только если метод zz не закомментирован. В противном случае он останется аргументом конструктора. Ухоженная!

ОБНОВЛЕНИЕ: Теперь я понимаю вашу точку зрения! Следующий код работает, как и ожидалось, получая доступ к переменной url в базовом классе:

class Bar(val url)

class Foo(_url : String) extends Bar(_url)
{
  def say() { println(url) }
}

Я согласен, это и безобразно, и напрашивается на неприятности. Фактически, я однажды столкнулся с этой проблемой сам, когда использовал классы Scala в качестве объектов Hibernate - я использовал параметр конструктора вместо поля в базовом классе, что привело к созданию дублированного поля: одного в базовом классе и одного в производном классе. Я бы даже не заметил, но во время выполнения Hibernate кричал, что определено дублированное сопоставление столбцов.

Так что я должен с вами несколько согласиться - это как-то сбивает с толку и может быть подвержено ошибкам. Это цена, которую вы платите за " неявность " и краткий код.

Однако обратите внимание, что никакие модифицированные и модификатор val перед аргументом конструктора не отличаются. Без измененного неизменяемого поля создается, а val дополнительно добавляет геттер.

1 голос
/ 05 декабря 2011

Scala создает поле из параметра конструктора, когда на такой параметр ссылается метод в классе.У меня проблемы с поиском ошибки в том, как это работает.

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

scala> class Bar(val url: String)
defined class Bar

scala> class Foo(url: String) extends Bar(url) {
     | def say() { println(url) }
     | }
defined class Foo

scala> new Foo("urlvalue").say
urlvalue

Если мы введем некоторую путаницу в случае параметра конструктораэтот пример все еще работает как ожидалось:

scala> class Bar(val Url: String)
defined class Bar

scala> class Foo(url: String) extends Bar(url) {
     | def say() { println(url) }
     | }
defined class Foo

scala> new Foo("urlvalue").say
urlvalue

Интересно, что вы могли бы подумать, что это сработало, потому что оно ввело поле url в нижнем регистре в Foo в дополнение к Url в верхнем регистреBar, но, похоже, это не так - компилятор, кажется, достаточно умен, чтобы знать, что он может перейти к Url, чтобы получить значение url в say, без поля нижнего регистрагенерируется.

scala> :javap -private Bar
Compiled from "<console>"
public class Bar extends java.lang.Object implements scala.ScalaObject{
    private final java.lang.String Url;
    public java.lang.String Url();
    public Bar(java.lang.String);
}

scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
    public void say();
    public Foo(java.lang.String);
}

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

scala> class Bar(var Url: String)
defined class Bar

scala> class Foo(url: String) extends Bar(url) {
     | def say() { println(url) }
     | }
defined class Foo

scala> val f = new Foo("urlvalue")
f: Foo = Foo@64fb7efa

scala> f.say
urlvalue

scala> f.Url = "newvalue"
f.Url: String = newvalue

scala> f.say
urlvalue

scala> :javap -private Foo
Compiled from "<console>"
public class Foo extends Bar implements scala.ScalaObject{
    private final java.lang.String url;
    public void say();
    public Foo(java.lang.String);
}
...