![](/img/trans.png)
[英]How to transform this simple OOP program to a functional-programming language?
[英]Functional programming in OOP language
不可变对象是可以的,但是,是否可以非最终本地引用 ?
换句话说,下一个代码片段可以表示为功能样式?
Employee e = new Employee("Lex", 24, 250);
e = Employee.setName(e, "Vasili");
e = Employee.setAge(e, 12);
e = Employee.setSalary(e, 2500);
Employee.log(e);
PS这里所有Employee
方法都是静态的,setter是返回新实例的工厂方法。
由于这个问题被标记为'java',我假设问题是关于Java中的FP实践(即不变性)。
今天Java的优秀做法是使用构建器:
Employee e = Employee.builder()
.surname("Lex")
.age(24)
.name("Vasili")
.salary(2500)
.build();
或静态构造函数:
Employee e = Employee.of("Vasili", "Lex", 24, 2500);
在这两种情况下,都应将“经典”构造函数声明为私有,以确保无法实例化对象并使其可用于处于不一致状态的客户端。
然后,对象变换器应该返回新对象:
Employee.of("Vasili", "Lex", 24, 2500) // creates an object
.updateName("Sergey") // returns 1st modified copy
.updateSalary(3500); // returns 2nd modified copy based on 1st copy
遵循这些实践,对非最终本地引用的需求通常会消失。
一个非常流行的例子是Date和Time API 。
现在,使用可变局部变量。 没关系,但代码可以缩短,并使用方法链更具表现力。 尝试按原样链接静态方法看起来不会很优雅:
Employee e = setSalary(setAge(setName(new Employee("Lex", 24, 250), "Vasili"), 12), 2500);
作为模拟monad的尝试,可以将对象包装在一个类似monad的容器中,该容器定义一个接受一个函数的bind方法,该函数将接受存储在monad中的对象并返回一些结果,这将再次包装在monad中。 一个简单的例子如下所示:
static class Employee {
public String name;
public int age;
public long salary;
}
static class Monad<T> {
private final T value;
private Monad(T value) { this.value = value; }
public static <T> Monad<T> of(T value) {
return new Monad<>(value);
}
public T getValue() { return value; }
public Monad<T> bind(UnaryOperator<T> operator){
return of(operator.apply(value));
}
}
public static void main(String[] args) {
Employee value = Monad.of(new Employee())
.bind(e -> {e.name = "Lex"; return e; })
.bind(e -> {e.age = 24; return e; })
.bind(e -> {e.salary = 2500; return e; })
.getValue();
}
但是这可以从版本8开始使用核心Java - Stream API可以做到这一点以及更多:
Stream.of(new Employee())
.map(e -> {e.name = "Lex"; return e; })
.map(e -> {e.age = 24; return e; })
.map(e -> {e.salary = 2500; return e; })
.findFirst()
.get();
它类似于每个函数返回新项的术语,在许多FP语言(例如Haskell)中,您甚至无法更新值,只需创建新值:
let myBook = beginBook "Haskell"
let myBook' = addChapter (Chapter "Intro" ["Hello","World"]) myBook
所以这里的beginBook
函数将返回一本书,然后addChapter
将返回另一本修改了一些字段的书。
对Haskell
一无所知,但我相信你正在努力实现这样的目标:
Employee e = new Employee("Lex")
.setAge(25)
.setSalary(2500)
.setGender(Gender.Male);
这只是以下列方式chaining
功能的结果
public Employee setParam(param){
this.param = param;
return this;
}
但这些方法不是静态的,它们属于实例。
此外,不需要将实例作为参数传递。
也:
this
不是必需的关键字; 在上面的例子中,这两个参数具有相同的名称,因此如果没有this
,代码基本上会将param的值重新赋值给自己。 如果这些参数有不同的名称, this
是不需要的。 然而,返回this
是必要的,因为它代表了对当前实例的引用。 例如:
public Employee setParam(String param) {
parameter = param; // parameter is a field in class Employee
return this; // this "this", is still necessary
}
final
变量 可能会限制你尝试用你的风格实现的目标
final Employee e = Employee.setName(e, "Name"); // invalid, e is unkown
// ----------------
final Employee e;
e = Employee.setName(e, "Name"); // invalid, e may not be initialized
// ----------------
final Employee e;
e = Employee.setName("Name"); // valid
// ----------------
final Employee e = null;
e = Employee.setName(e, "Name"); // invalid. e was already initalized to null
// ----------------
final Employee e = Employee.setName("Name"); // valid
e = Employee.setName("Name2"); // invalid, final variable already initialized
这样的事情会对你有用吗(忽略对Optional
和ifPresent
的错误使用,我们可以用更有意义的东西替换它)?
Optional.of( new Employee("Lex", 24, 250) )
.map( e -> Employee.setName(e, "Vasili") )
.map( e -> Employee.setAge(e, 12) )
.map( e -> Employee.setSalary(e, 2500) )
.ifPresent( e -> Employee.log(e) );
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.