【java】java 函数式接口例子 求解运行过程
看到别人写的一个工厂模式的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 builder = (name, supplier) -> { name.toString();
return;
};
所以可以看到只是写法不一样,由于这个Builder
的函数式接口定义为void
返回,所以可以写出return;
也可以不用写,这么就可以不用大括号了
Builder builder = (name, supplier) -> name.toString();
这是那张纸。。。所以你得需要实际数据去执行,执行时,只用看函数式接口定义的方法名就可以了,为了方便举例,我们换个函数式接口来看,比如java.util.function.Function
@FunctionalInterfacepublic interface Function<T, R> {
/**
* Applies this function to the given argument.
*
* @param t the function argument
* @return the function result
*/
R apply(T t);
可以看到这个Function
的定义中,用了泛型,那就是说,只要是满足一个参数T
,返回一个R
的方法都可以叫Function
,哈哈,所以这个很抽象的东西,只要符合都是Function
的实例
比如:Object
的方法toString
Function function = o -> o.toString();
假如你有个A
类的实例a
,想执行a
的toString
方法
A a = new A();a.toString();
哈哈哈,这只是弟弟的执行方式
你可以这么骚起来
A a = new A();Function function = o -> o.toString();
function.apply(a);
所以要是你不知道函数式接口怎么执行的,比如上面那个例子,不知道function
怎么执行,你直接找到这个function
的接口方法apply
在哪里调用的,function.apply(a)
,这里apply
的传入的参数是a
,这个时候,你用a
当作参数,去执行方法体o.toString()
就可以了
这里要提到一点,有个方法引用的语法糖吧,比如o -> o.toString()
,这里其实用的是Object
的toString()
方法,所以你可以直接写成Object::toString
A a = new A();Function function = Object::toString;
function.apply(a);
说了这么多,再回到题主问题上
“那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
,执行后,不返回
@FunctionalInterfacepublic interface Consumer<T> {
/**
* Performs this operation on the given argument.
*
* @param t the input argument
*/
void accept(T t);
它的接口方法是accept
,所以找到其执行的方法位置consumer.accept(map::put);
也就是说这个参数是map::put
,这是一个方法引用,其实就是Map.put
方法,那这个明明是Builder
泛型的Consumer
,它传入Map.put
是个Builder
么?
我们看看Map.put
的定义
V put(K key, V value);
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
就完事了
public class App { private static Map<WeaponType, Supplier<Weapon>> map = new HashMap<>();
static {
map.put(WeaponType.SWORD, Sword::new);
map.put(WeaponType.AXE, Axe::new);
map.put(WeaponType.SPEAR, Spear::new);
map.put(WeaponType.BOW, Bow::new);
}
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
Weapon axe = map.get(WeaponType.AXE).get();
}
}
当然这么写感觉不是很高逼格,或者说还不是很好,本来是想要表示一个一对一的对应关系的,所以花费一个额外的map
对象来存储对应的关系,其实在Java中表示一一对应关系的可以才用枚举嘛,枚举就可以解决,我平常也比较喜欢用枚举来表示固定的一一对应关系,也好写注释,用枚举WeaponTypeMapper
来代替map
@Getter@AllArgsConstructor
public enum WeaponTypeMapper {
SWORD(WeaponType.SWORD, Sword::new),
AXE(WeaponType.AXE, Axe::new),
SPEAR(WeaponType.SPEAR, Spear::new),
BOW(WeaponType.BOW, Bow::new),
;
private WeaponType weaponType;
private Supplier<Weapon> supplier;
public static Weapon getWeapon(WeaponType weaponType){
Weapon weapon = Arrays.stream(WeaponTypeMapper.values())
.filter(weaponTypeMapper -> weaponTypeMapper.getWeaponType().equals(weaponType))
.findFirst()
.map(WeaponTypeMapper::getSupplier)
.map(Supplier::get).get();
return weapon;
}
}
然后获取Weapon的时候,直接
public class App { public static void main(String[] args) {
Weapon axe = WeaponTypeMapper.getWeapon(WeaponType.AXE);
}
}
囧。。。以上。。。仅供参考
https://colobu.com/2014/10/28...
// 也可以这样interface Builder<A, B> {
void add(A type, Supplier<B> supplier);
}
consumer.accept((Builder<WeaponType, Weapon>)map::put);
interface P { void say(String msg);
}
// 通过lambda表达式创建了一个P实例,拥有say方法
P p = name -> {
System.out.println(name);
};
p.say("lalala");
以上是 【java】java 函数式接口例子 求解运行过程 的全部内容, 来源链接: utcz.com/a/72635.html