Java函数指针

2

Java函数指针

  函数指针是指向函数的指针变量,是C++和Python中一种常用的技术。使用范围主要是根据传入不同类型的元素,希望调用不同的函数。

使用场景

举个例子,给两个变量a和b,希望传入字符’+‘调用a + b的方法,’-‘调用a - b的方法,’*‘调用a * b的方法,’/‘调用a / b的方法

常规的写法是使用switch来对输入的字符进行校验,然后调用对应的方法。

当字符的种类很多,甚至是int型时,我们不得不写非常多的case语句。在公司要求的编码规范中,可能要求函数的长度不能超过50行,则需要嵌套大量的switch case,如下代码所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
class Solution {
public static void main(String[] args) {
System.out.println("8 + 2 = " + test(8, 2, '+'));
System.out.println("8 - 2 = " + test(8, 2, '-'));
System.out.println("8 * 2 = " + test(8, 2, '*'));
System.out.println("8 / 2 = " + test(8, 2, '/'));
}

private static int test(int a, int b, char c) {
int res = 0;
switch (c) {
case '+':
res = add(a, b);
break;
case '-':
res = subtract(a, b);
break;
default:
res = otherSign(a, b, c);
break;
}
return res;
}

private static int otherSign(int a, int b, char c) {
int res = 0;
switch (c) {
case '*':
res = multiple(a, b);
break;
case '/':
res = divide(a, b);
break;
default:
break;
}
return res;
}

private static int add(int a, int b) {
return a + b;
}

private static int subtract(int a, int b) {
return a - b;
}

private static int multiple(int a, int b) {
return a * b;
}

private static int divide(int a, int b) {
return a / b;
}
}

上述代码中的otherSign就是模拟了test函数超过50行后,转到另一个函数继续处理switch case的情况。

对于这种情况,我们可以使用函数指针+表驱动的方式,建立一个map,其中key是符号,value是对应的函数指针。

但是Java中不存在指针的概念,我们应该如何实现呢?

函数指针

我们可以使用接口的方式,将对象放在map中,然后使用的时候取出该对象,调用对象的方法即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
class Solution {
public static void main(String[] args) {
System.out.println("8 + 2 = " + test(8, 2, '+'));
System.out.println("8 - 2 = " + test(8, 2, '-'));
System.out.println("8 * 2 = " + test(8, 2, '*'));
System.out.println("8 / 2 = " + test(8, 2, '/'));
}

private static HashMap<Character, Function> map = new HashMap<>() {
{
put('+', new Function() {
@Override
public int calc(int a, int b) {
return add(a, b);
}
});

put('-', new Function() {
@Override
public int calc(int a, int b) {
return subtract(a, b);
}
});

put('*', new Function() {
@Override
public int calc(int a, int b) {
return multiple(a, b);
}
});

put('/', new Function() {
@Override
public int calc(int a, int b) {
return divide(a, b);
}
});
}
};

private static int test(int a, int b, char c) {
Function function = map.get(c);
int res = 0;
if (function != null) {
res = function.calc(a, b);
}
return res;
}

private static int add(int a, int b) {
return a + b;
}

private static int subtract(int a, int b) {
return a - b;
}

private static int multiple(int a, int b) {
return a * b;
}

private static int divide(int a, int b) {
return a / b;
}
}

interface Function {
int calc(int a, int b);
}

小伙伴可能会有疑问,上述代码比之前那个还要长,为什么还要这么些呢?

这是因为我们还没有对这个写法进行改造,放入map中的Function对象仅仅只有一个方法,因此可以使用lambda的方式进行优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Solution {
public static void main(String[] args) {
System.out.println("8 + 2 = " + test(8, 2, '+'));
System.out.println("8 - 2 = " + test(8, 2, '-'));
System.out.println("8 * 2 = " + test(8, 2, '*'));
System.out.println("8 / 2 = " + test(8, 2, '/'));
}

private static HashMap<Character, Function> map = new HashMap<>() {
{
put('+', (a, b) -> add(a, b));
put('-', (a, b) -> subtract(a, b));
put('*', (a, b) -> multiple(a, b));
put('/', (a, b) -> divide(a, b));
}
};

private static int test(int a, int b, char c) {
Function function = map.get(c);
int res = 0;
if (function != null) {
res = function.calc(a, b);
}
return res;
}

private static int add(int a, int b) {
return a + b;
}

private static int subtract(int a, int b) {
return a - b;
}

private static int multiple(int a, int b) {
return a * b;
}

private static int divide(int a, int b) {
return a / b;
}
}

interface Function {
int calc(int a, int b);
}

下面这个写法的代码量就少于使用switch case的写法了,而且随着case的增加,这种优势会越来越明显。

如果lambda表达式中只有一行调用函数,且调用函数的参数和lambda表达式的参数相同,那么lambda表达式就可以进行改写成双冒号的形式。

如果是静态的方法,可以用类名::的方式
如果是非静态的方法,可以用对象::的方式、this::的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class Solution {
public static void main(String[] args) {
System.out.println("8 + 2 = " + test(8, 2, '+'));
System.out.println("8 - 2 = " + test(8, 2, '-'));
System.out.println("8 * 2 = " + test(8, 2, '*'));
System.out.println("8 / 2 = " + test(8, 2, '/'));
}

private static HashMap<Character, Function> map = new HashMap<>() {
{
put('+', Solution::add);
put('-', Solution::subtract);
put('*', Solution::multiple);
put('/', Solution::divide);
}
};

private static int test(int a, int b, char c) {
Function function = map.get(c);
int res = 0;
if (function != null) {
res = function.calc(a, b);
}
return res;
}

private static int add(int a, int b) {
return a + b;
}

private static int subtract(int a, int b) {
return a - b;
}

private static int multiple(int a, int b) {
return a * b;
}

private static int divide(int a, int b) {
return a / b;
}
}

interface Function {
int calc(int a, int b);
}

Java小结

  这种使用Map来区分函数执行方式的代码在实际的工程开发中经常使用,这种写法可以称为函数指针也可以称为表驱动,希望小伙伴们能掌握它。

-------------本文结束感谢您的阅读-------------
0%