简体   繁体   English

如何用scala中的不可变couterpart替换java可变类?

[英]How can I replace a java mutable class with its immutable couterpart in scala?

I'm trying to understand how to use immutable classes in scala as a replacement for mutable java classes. 我试图理解如何在scala中使用不可变类作为可变java类的替代。

My example use case is searching for contacts in a database. 我的示例用例是在数据库中搜索联系人。 When programming imperatively you would pull each field from the database table and set it on the java object. 当命令性编程时,您将从数据库表中提取每个字段并将其设置在java对象上。

How would you do this in Scala using immutable types and functional style? 你会如何使用不可变类型和函数样式在Scala中执行此操作?

I know I could use a constructor and create a new instance of the object passing all required data to it, but for large complex data structures this doesn't seem elegant. 我知道我可以使用构造函数并创建一个新的对象实例,将所有必需的数据传递给它,但对于大型复杂的数据结构,这似乎并不优雅。

What is the "Scala" / "Functional" way to accomplish this? 什么是“Scala”/“功能”方法来实现这一目标? Or what are some best practices related to constructing complex immutable types? 或者与构建复杂的不可变类型有关的一些最佳实践是什么?

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;
}

Contact POJO 联系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;
    }
}

The quick answer is that you call the constructor with your parameters. 快速回答是您使用参数调用构造函数。 In Java: 在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())
   );

I left out the optional birthday for now, we'll add it back in the Scala version. 我现在省略了可选的生日,我们将其添加回Scala版本。

We still have a mutable Contacts list though, so lets look at making it properly immutable. 我们仍然有一个可变的联系人列表,所以让我们看看它是否正确不变。 We'll use a Scala list. 我们将使用Scala列表。 First, the scala data object: 首先,scala数据对象:

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

We have optional birthday as part of the type-sig as a bonus. 我们有可选的生日作为类型sig的一部分作为奖励。

Now lets define a simple extractor method, given a resultSet: 现在让我们定义一个简单的提取器方法,给定一个resultSet:

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)
}

This version recursively builds up a singly-linked list of all contacts in your ResultSet. 此版本以递归方式构建ResultSet中所有联系人的单链接列表。 This is immutable. 这是不可改变的。

You can also have a mutable builder which makes the immutable object you are looking for. 您还可以拥有一个可变构建器 ,它可以生成您正在查找的不可变对象。 There's nothing wrong with having temporary mutable objects if they stay in their creation scope and they are not propagated elsewhere. 如果临时可变对象保留在其创建范围内并且它们不会传播到其他地方,则没有任何问题。 The Scala standard library implementation use plenty of those. Scala标准库实现使用了大量的。

Here is a simple example (although this solution is more useful when constructors are big): 这是一个简单的例子(虽然这个解决方案在构造函数很大时更有用):

// 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*/ ) }
  }
}

It can be used as: 它可以用作:

val builder = new PersonBuilder
builder setName "Alex"
builder setAge 42
builder setMarried false
val person = builder build  // The immutable instance

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM