看到别人写的一个工厂模式的demo代码,用到了Lambda表达式以及Consumer和Supplier.
但遇到一个地方看不太懂 求各位解答下
Builder.java
public interface Builder {
void add(WeaponType name, Supplier<Weapon> supplier);
}
WeaponFactory.java
public interface WeaponFactory {
/**
* Creates an instance of the given type.
* @param name representing enum of an object type to be created.
* @return new instance of a requested class implementing {@link Weapon} interface.
*/
Weapon create(WeaponType name);
/**
* Creates factory - placeholder for specified {@link Builder}s.
* @param consumer for the new builder to the factory.
* @return factory with specified {@link Builder}s
*/
static WeaponFactory factory(Consumer<Builder> consumer) {
Map<WeaponType, Supplier<Weapon>> map = new HashMap<>();
consumer.accept(map::put);
return name -> map.get(name).get();
}
}
WeaponType.java
public enum WeaponType {
SWORD, AXE, BOW, SPEAR
}
App.java
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
WeaponFactory factory = WeaponFactory.factory(builder -> {
builder.add(WeaponType.SWORD, Sword::new);
builder.add(WeaponType.AXE, Axe::new);
builder.add(WeaponType.SPEAR, Spear::new);
builder.add(WeaponType.BOW, Bow::new);
});
Weapon axe = factory.create(WeaponType.AXE);
LOGGER.info(axe.toString());
}
}
想请教,在App.java中WeaponFactory.factory接受的是Consumer<Builder>类的函数式接口,那builder的add方法是如何实现的呢?并没有看到对add方法进行lambda表达式的实现?请问这是什么原理?
map又是如何接受builder中多个对象的呢?
题主好,以下是我个人对于函数式接口的理解
以往我们在看一个方法时,看它的关键点其实就是这么几个
我们随便拿一个方法来举例:
所以可以看到方法其实是以固定参数返回固定值的一个抽象过程,而这里的固定参数就是我们平常用的数据类型
因此至此可以说,方法和数据类型是分开的,两个没啥实际关联,是不同种类的东西,方法只是需要数据进行执行而已
举个实际例子,例如如何钓鱼这个方法,需要用鱼竿和诱饵这两个实际的东西配合钓鱼的方法才可以执行
不过钓鱼也可以是一个实际东西,比如我把如何用鱼竿和诱饵钓鱼的方法写在纸上,交给你
这个时候这个纸也成了一个实际东西,也就是说钓鱼这个方法也就成了一个实际东西了
函数式接口就是这么一个纸,把方法承载下来,当然它也是一个数据类型,也就是说你可以在方法里传递方法
函数式接口也是一个方法,那它也符合方法刚才那4个关键点,以
Builder.java
为例,我们写一个Builder的函数式接口的实例所以可以看到只是写法不一样,由于这个
Builder
的函数式接口定义为void
返回,所以可以写出return;
也可以不用写,这么就可以不用大括号了这是那张纸。。。所以你得需要实际数据去执行,执行时,只用看函数式接口定义的方法名就可以了,为了方便举例,我们换个函数式接口来看,比如
java.util.function.Function
可以看到这个
Function
的定义中,用了泛型,那就是说,只要是满足一个参数T
,返回一个R
的方法都可以叫Function
,哈哈,所以这个很抽象的东西,只要符合都是Function
的实例比如:
Object
的方法toString
假如你有个
A
类的实例a
,想执行a
的toString
方法哈哈哈,这只是弟弟的执行方式
你可以这么骚起来
所以要是你不知道函数式接口怎么执行的,比如上面那个例子,不知道
function
怎么执行,你直接找到这个function
的接口方法apply
在哪里调用的,function.apply(a)
,这里apply
的传入的参数是a
,这个时候,你用a
当作参数,去执行方法体o.toString()
就可以了这里要提到一点,有个方法引用的语法糖吧,比如
o -> o.toString()
,这里其实用的是Object
的toString()
方法,所以你可以直接写成Object::toString
说了这么多,再回到题主问题上
“那builder的add方法是如何实现的呢?并没有看到对add方法进行lambda表达式的实现?”
我们知道
builder
是函数式接口Builder
的实例,要知道它是如何实现的,我们就先找到Builder
的接口方法add
,然后我们再找
add
方法什么时候执行的找到了,
builder.add(WeaponType.SWORD, Sword::new);
,这里就已经执行了builder
的add
方法,往回看builder
的定义,但是这时候builder
并不是像我们之前的function
一样在上一行有定义,而是builder本身就在一个lamdba
表达式中,所以这就是方法中嵌套方法了,类似之前钓鱼的例子,如何钓鱼的纸上写的是一张地图,需要先要找到另一张纸才可以找到如何钓鱼的方法所以还是按照之前我说的方法,既然
builder
这个时候是lamdba
表达式中的一个参数,那还是看这个函数式接口是在哪里调用的即可经过查找,我们知道这个时候是调用的
WeaponFactory.factory
方法,而这个方法参数是个Consumer<Builder>
,Consumer
是个自带的函数式接口,代表任何一个参数T
,执行后,不返回它的接口方法是
accept
,所以找到其执行的方法位置consumer.accept(map::put);
也就是说这个参数是
map::put
,这是一个方法引用,其实就是Map.put
方法,那这个明明是Builder
泛型的Consumer
,它传入Map.put
是个Builder
么?我们看看
Map.put
的定义Builder
是传入WeaponType
和Supplier<Weapon>
,不返回Map.put
是传入泛型K
和泛型V
,返回V
感觉不一样。。。其实是一样的。。。因为不返回其实也是返回,它返回的是大写开头的
Void
,其实Builder
应该是Builder<WeaponType, Supplier<Weapon>, Void>,此时由于Void
,必须返回,且必须返回null
,因此Map.put
是一个Builder
的实例所以最终串起来,
WeaponType.SWORD, Sword::new
两个参数实际是执行了一个Map.put
方法...emmm,所以第一次看完你这个工厂,我咋感觉写了一个像这样的东西而已,不用
Builder
和WeaponFactory
,因为底层用的map
,所以map
就完事了当然这么写感觉不是很高逼格,或者说还不是很好,本来是想要表示一个一对一的对应关系的,所以花费一个额外的
map
对象来存储对应的关系,其实在Java中表示一一对应关系的可以才用枚举嘛,枚举就可以解决,我平常也比较喜欢用枚举来表示固定的一一对应关系,也好写注释,用枚举WeaponTypeMapper
来代替map
然后获取Weapon的时候,直接
囧。。。以上。。。仅供参考