Как я могу заменить изменяемый класс java его неизменным couterpart в scala? - PullRequest
3 голосов
/ 04 сентября 2011

Я пытаюсь понять, как использовать неизменяемые классы в Scala в качестве замены для изменяемых классов Java.

Мой пример использования: поиск контактов в базе данных. При программировании необходимо извлекать каждое поле из таблицы базы данных и устанавливать его для объекта Java.

Как бы вы сделали это в Scala, используя неизменяемые типы и функциональный стиль?

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

Что такое «Scala» / «Функциональный» способ сделать это? Или каковы некоторые лучшие практики, связанные с созданием сложных неизменяемых типов?

public List<Contact> search(String firstName, String lastName) throws SQLException {
    List<Contact> contacts = new ArrayList<Contact>();

    Connection con = null;
    PreparedStatement ps = null;
    ResultSet rs = null;
    try {
        con = dataSource.getConnection();

        ps = con.prepareStatement("select * from contacts where first_name like ? and last_name like ?");
        ps.setString(1, "%" + firstName + "%");
        ps.setString(1, "%" + lastName + "%");
        rs = ps.executeQuery();

        while (rs.next()) {
            Contact contact = new Contact();
            contact.setFirstName(rs.getString("first_name"));
            contact.setMiddleName(rs.getString("middle_name"));
            contact.setLastName(rs.getString("last_name"));
            Date birthday = rs.getDate("birthday");
            if (birthday != null) {
                contact.setBirthday(new Date(birthday.getTime()));
            }
            contacts.add(contact);
        }

    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        rs.close();
        ps.close();
        con.close();
    }
    return contacts;
}

Контакт POJO

import java.util.Date;

public class Contact {
    private String firstName;
    private String middleName;
    private String lastName;
    private Date birthday;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
}

Ответы [ 2 ]

8 голосов
/ 04 сентября 2011

Быстрый ответ - вы вызываете конструктор со своими параметрами. В Java:

public class Contact {
  private final String firstName;
  private final String middleName;
  private final String lastName;
  private final Date birthday;

  public Contact(
    String firstName,
    String middleName,
        String lastName,
    Date birthday
  ) {
    this.firstName = firstName;
    this.middleName = middleName;
    this.lastName = lastName;
    this.birthday = birthday;
  }
  … // getters
}

  … inside while loop:
  contacts.add(new Contact(
    rs.getString("first_name"),
    rs.getString("middle_name"),
    rs.getString("last_name"),
    new Date(birthday.getTime())
   );

Я пока оставил необязательный день рождения, мы добавим его обратно в версию Scala.

У нас все еще есть изменяемый список контактов, поэтому давайте посмотрим, как сделать его должным образом неизменным. Мы будем использовать список Scala. Во-первых, объект данных scala:

case class Contact(
  first: String,
  middle: String,
  last: String,
  birthday: Option[Date])

У нас есть дополнительный день рождения в качестве бонуса.

Теперь давайте определим простой метод извлечения, учитывая набор результатов:

def contacts(rs: ResultSet) = {
  @annotation.tailrec def loop(cs: List[Contact]): List[Contact] =
    if (!rs.next()) cs else loop(
      Contact(
        rs.getString("first_name"),
        rs.getString("middle_name"),
        rs.getString("last_name"),
        Option(rs.getLong("birthday"))
      ) :: cs)
  loop(Nil)
}

Эта версия рекурсивно создает односвязный список всех контактов в вашем ResultSet. Это неизменное.

6 голосов
/ 04 сентября 2011

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

Вот простой пример (хотя это решение более полезно, когда конструкторы большие):

// Here is the immutable class
case class Person( name: String, age: Int, married: Boolean )

// Here comes the mutable builder
class PersonBuilder {
  private var name: Option[String] = None
  private var age: Option[Int] = None
  private var married: Option[Boolean] = None

  def setName( n: String ) = { name = Some(n); this }
  def setAge( a: Int ) = { age = Some(a); this }
  def setMarried( m: Boolean ) = { married = Some(m); this }

  def build() = {
    val person = for( n <- name; a <- age; m <- married ) 
                   yield { Person( n, a, m ) }
    person getOrElse { throw new IllegalStateException( /*msg*/ ) }
  }
}

Может использоваться как:

val builder = new PersonBuilder
builder setName "Alex"
builder setAge 42
builder setMarried false
val person = builder build  // The immutable instance
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...