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

发布于 2024-12-02 17:46:05 字数 2306 浏览 0 评论 0原文

我试图了解如何在 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;
    }
}

I'm trying to understand how to use immutable classes in scala as a replacement for mutable java classes.

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.

How would you do this in Scala using immutable types and functional style?

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?
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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

以为你会在 2024-12-09 17:46:05

快速的答案是您使用参数调用构造函数。在 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])

我们有可选的生日作为 type-sig 的一部分作为奖励。

现在让我们定义一个简单的提取器方法,给定一个结果集:

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

此版本递归地构建结果集中所有联系人的单链接列表。这是不可改变的。

The quick answer is that you call the constructor with your parameters. In 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.

We still have a mutable Contacts list though, so lets look at making it properly immutable. We'll use a Scala list. First, the scala data object:

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.

Now lets define a simple extractor method, given a 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. This is immutable.

嘿咻 2024-12-09 17:46:05

您还可以拥有一个可变构建器,它可以生成您正在寻找的不可变对象。如果临时可变对象保留在其创建范围内并且不会传播到其他地方,那么拥有临时可变对象并没有什么问题。 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

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.

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文