【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中多个对象的呢?

回答

题主好,以下是我个人对于函数式接口的理解

以往我们在看一个方法时,看它的关键点其实就是这么几个

  1. 方法名
  2. 方法参数
  3. 方法体
  4. 方法返回类型

我们随便拿一个方法来举例:

【java】java 函数式接口例子 求解运行过程

所以可以看到方法其实是以固定参数返回固定值的一个抽象过程,而这里的固定参数就是我们平常用的数据类型
因此至此可以说,方法和数据类型是分开的,两个没啥实际关联,是不同种类的东西,方法只是需要数据进行执行而已

举个实际例子,例如如何钓鱼这个方法,需要用鱼竿和诱饵这两个实际的东西配合钓鱼的方法才可以执行
不过钓鱼也可以是一个实际东西,比如我把如何用鱼竿和诱饵钓鱼的方法写在纸上,交给你
这个时候这个纸也成了一个实际东西,也就是说钓鱼这个方法也就成了一个实际东西了

函数式接口就是这么一个纸,把方法承载下来,当然它也是一个数据类型,也就是说你可以在方法里传递方法

函数式接口也是一个方法,那它也符合方法刚才那4个关键点,以Builder.java为例,我们写一个Builder的函数式接口的实例

Builder builder = (name, supplier) -> {

name.toString();

return;

};

【java】java 函数式接口例子 求解运行过程

所以可以看到只是写法不一样,由于这个Builder的函数式接口定义为void返回,所以可以写出return;也可以不用写,这么就可以不用大括号了

Builder builder = (name, supplier) -> name.toString();

这是那张纸。。。所以你得需要实际数据去执行,执行时,只用看函数式接口定义的方法名就可以了,为了方便举例,我们换个函数式接口来看,比如java.util.function.Function

@FunctionalInterface

public 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,想执行atoString方法

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(),这里其实用的是ObjecttoString()方法,所以你可以直接写成Object::toString

A a = new A();

Function function = Object::toString;

function.apply(a);

说了这么多,再回到题主问题上

“那builder的add方法是如何实现的呢?并没有看到对add方法进行lambda表达式的实现?”

我们知道builder是函数式接口Builder的实例,要知道它是如何实现的,我们就先找到Builder的接口方法add
然后我们再找add方法什么时候执行的

【java】java 函数式接口例子 求解运行过程

找到了,builder.add(WeaponType.SWORD, Sword::new);,这里就已经执行了builderadd方法,往回看builder的定义,但是这时候builder并不是像我们之前的function一样在上一行有定义,而是builder本身就在一个lamdba表达式中,所以这就是方法中嵌套方法了,类似之前钓鱼的例子,如何钓鱼的纸上写的是一张地图,需要先要找到另一张纸才可以找到如何钓鱼的方法

所以还是按照之前我说的方法,既然builder这个时候是lamdba表达式中的一个参数,那还是看这个函数式接口是在哪里调用的即可

【java】java 函数式接口例子 求解运行过程

经过查找,我们知道这个时候是调用的WeaponFactory.factory方法,而这个方法参数是个Consumer<Builder>Consumer是个自带的函数式接口,代表任何一个参数T,执行后,不返回

@FunctionalInterface

public 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是传入WeaponTypeSupplier<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,所以第一次看完你这个工厂,我咋感觉写了一个像这样的东西而已,不用BuilderWeaponFactory,因为底层用的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

回到顶部