Reputation: 87187
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;
}
}
Upvotes: 3
Views: 649
Reputation: 4706
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.
Upvotes: 8
Reputation: 40461
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
Upvotes: 6