Как сделать аргумент конструктора доступным из инициализаторов экземпляра? - PullRequest
0 голосов
/ 14 сентября 2009
class Foo {
    final String s;

    final int x = s.length();

    Foo(String s) {
        this.s = s;
    }
}

Приведенный выше код не будет компилироваться, потому что присвоение x происходит до присвоения s. Я мог бы поместить присваивание x внутри конструктора, но для удобства чтения в реальном случае с 100 полями я бы предпочел не делать этого.

Я нашел обходной путь, который мне не нравится. Можете ли вы найти лучший?

abstract class Hack {
    final String s;

    Hack(String s) {
        this.s = s;
    }
}

class Foo extends Hack {
    final int x = s.length();

    Foo(String s) {
        super(s);
    }
}

Ответы [ 8 ]

4 голосов
/ 14 сентября 2009

Как для удобства чтения:

class Foo {    
  final String s;    
  final int sizeOfS;
  Foo(String s) {        
    this.s = s;    
    sizeOfS = s.length();
  }
}

Читаемость важнее всего при присвоении имен переменных!

1 голос
/ 14 сентября 2009

Рассмотрим шаблон строителя:

 public interface Foo {}

 public class FooBuilder  {

       private final String s;

       public FooBuilder(String s) {
           this.s = s;
       }

       public Foo build() {
           return new FooImpl();
       }

       private class FooImpl implements Foo {
           final int x = FooBuilder.this.s.length();
       }
  }

Здесь, вероятно, есть пара не очень приятных поворотов: реализация должна перейти к внутреннему классу. Хотя интерфейс может быть абстрактным классом и выполнять ту же цель.

1 голос
/ 14 сентября 2009

Я думаю, что вы не можете. Как указывалось Базовое руководство по Java , если у вас есть более сложная логика, чем константы для инициализации, вы должны написать код в конструктор (или статический блок инициализации).

0 голосов
/ 14 сентября 2009

Поскольку x всегда зависит от s, у вас есть 3 варианта.

1) инициализация x является побочным эффектом инициализации s

class Foo {
    final String s;
    final int x;

    private void setS(String s) {
        this.s = s;
        this.x = s.length();
    }

    Foo(String s) {
        this.setS(s);
    }
}

2) рассчитать x из s

class Foo {
    final String s;

    private int getX() {
        return s.length();
    }

    Foo(String s) {
        this.setS(s);
    }
}

2a) рассчитать x из s, но кэш x

class Foo {
    final String s;
    private int cached_x = null;

    int getX() {
        if (x == null) {
            cached_x = s.lenth();
        return cached_x;
    }

    Foo(String s) {
        this.s = s;
    }
}

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

class Foo {
    final String s;
    final int x;

    Foo(String s) {
        this.s = s;
        this.x = this.s.length();
    }
}
0 голосов
/ 14 сентября 2009

Обратите внимание, что ваш пример всегда будет неудачным: он пытается получить длину s перед инициализацией, вызывая NullPointerException.

Что не так с этим?

final String s;
final int x;

Foo(String s) {
  this.s = s;
  this.x = s.length();
}
0 голосов
/ 14 сентября 2009

Поскольку в вашем исходном примере x функционально зависит от s, имеет ли смысл создавать отдельный класс, который объединяет эти два параметра? Тогда родитель имеет только одно назначение. Вам по-прежнему нужно присваивать s и вычислять другое значение в <init>, но это кажется гораздо более читабельным, когда нет 100 полей.

0 голосов
/ 14 сентября 2009

У меня был бы метод setS (String s), который правильно устанавливает x int. Используйте это ваш конструктор. Если вы не хотите, чтобы s или x были общедоступными, сделайте метод setS закрытым / защищенным / по умолчанию.

class Foo {
    int x;
    String s;
    private setS(String s) { this.s=s;this.x=s.length();}

    Foo(String s) {
            setS(s);
    }
}

Другое решение (которое, кажется, более точно соответствует тому, что вы пытаетесь сделать) - это исключить x int. Обеспечение:

int getX() { return s.length(); }

вместо члена x.

Если x действительно суррогат для гораздо более сложного объекта, попробуйте:

class Foo {
    private Object complex;
    private String s;
    Foo(String s){ this.s = s;}
    Object getComplex() {
        if (complex == null) {
            *complex=new Complex(s);
        }
        return complex;
    }
}

* Может потребоваться добавить синхронизированную или дважды проверенную блокировку здесь для обеспечения безопасности потока.

0 голосов
/ 14 сентября 2009

Кроме комментариев:

interface Foo {
}

public static Foo newFoo(final String _s) {
    return new Foo() {
        private final String s = _s;
        private final int x = _s.length();
   };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...