匿名内部类:
顾名思义是没有名字的内部类(inner class),这个类的作用大概就是让Java更人性化。
当某个类只会使用一次时,创建一个类会显得很笨重呆板,所以就有了匿名内部类这个东西。
看一段简短的代码:
interface melo //定义一个接口
{
public String Name();
}
public class Test {
public void use(melo a)
{
System.out.println(a.Name());
}
public static void main(String[] args)
{
Test a=new Test();
// use 方法需要一个melo 参数,但melo接口不能直接创建对象,所以需要一个实现
melo接口的类的对象。
a.use(new melo()*{
public String Name()
{
return "cgz";
}*
});
}
}
这是匿名内部类的写法,正常写法是:
class melon implements melo // 普通的有名字的实现melo接口的类
{
public String Name()
{
return "cgz";
}
}
a.use(new melon());
从经验就可以得到几个结论,匿名内部类需要创建对象所以不可以是抽象类。
匿名内部类没有名字,所以也不可能有构造函数。但是这句话也有例外…
abstract class melo
{
private String name; //name成员
public melo(String name) {this.name=name;} //构造函数
public abstract void Name() ; //抽象函数
public String getName() {
return name;
}
}
public class Test {
public void use(melo a)
{
a.Name();
}
public static void main(String[] args)
{
Test a=new Test();
a.use(new melo("cgz") //此处用了父类的构造函数
{
public void Name()
{System.out.println(getName());}
});
}
}
public void boysAndGirls(List<Person> persons) {
Map<Integer, Integer> result = persons.parallelStream().filter(p -> p.getAge()>=25 && p.getAge()<=35).
collect(
Collectors.groupingBy(p->p.getSex(), Collectors.summingInt(p->1))
);
System.out.print("boysAndGirls result is " + result);
System.out.println(", ratio (male : female) is " + (float)result.get(Person.MALE)/result.get(Person.FEMALE));
}
创建匿名内部类时,必须实现接口或抽象父类的所有抽象方法,甚至可以重写其中的普通方法。
另外一点,匿名内部类中访问的局部变量都已被显式或隐式的被final修饰,所以不可被改变。
关于lambda表达式,可以改写匿名内部类的代码使其更加简洁。
由编译器推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能
匿名内部类写法:
interface Command
{
void process(int [] target);
}
class ProcessArray
{
public void process(int [] target,Command cmd)
{
cmd.process(target);
}
}
public class Test {
public static void main(String[] args)
{
ProcessArray pa=new ProcessArray();
int []target= {3,4,6,8};
pa.process(target, new Command()
{
public void process(int [] target)
{
int sum=0;
for(int tmp:target)
{
sum+=tmp;
}
System.out.println(sum);
}
});
}
}
Lambda写法:
public class Test {
public static void main(String[] args)
{
ProcessArray pa=new ProcessArray();
int []target= {3,4,6,8};
pa.process(target, (int [] array)->{
int sum=0;
for(int tmp:target)
{
sum+=tmp;
}
System.out.println(sum);
});
}
}
Lambda表达式没有像匿名内部类那样创建了一个对象,并且实现接口和抽象父类的方法。
基本语法为:
参数列表、箭头、Lambda主体。
(parameters) -> expression
或(请注意语句的花括号)
(parameters) -> { statements; }
其中Lambda表达式可以没有参数,当 { statements; } 语句块只有一句时可以省略花括号,
当(parameters)只有一个形参时,可以提省略圆括号。
当 { statements; }只有一句,表达式又需要返回值时,可以省略return。
最后一点尤为重要,它指明了lambda表达式的目标类型必须是函数式接口,函数式接口只能包含一个抽象方法。
// 首先Runnableu,是函数式接口,并且只有一个无参数的方法
用lambda表达式创建了一个lambda对象,实现了唯一的方法。
Runnable r=()->{
for(int i=0;i<100;i++)
{
System.out.println();
}
}
下面的代码看起来和上面的没啥区别,但他是错的,原因是Object不是函数式接口
Object obj=() ->{
for(int i=0;i<100;i++)
{
System.out.println();
}
}
Arrays类的某些方法需要Comparator ,XxxOperator, XxxFunction等函数式接口。演示一下lambda表达式的用法
public static void main(String[] args)
{
String[] arr1=new String[] {"melo","pipihong","pipiwei","pipisheng","shuaishuaizhi"};
Arrays.parallelSort(arr1, (o1,o2)->o1.length()-o2.length()); //用字符串长度排序
System.out.println(Arrays.deepToString(arr1));
int arr2[]=new int []{3,5,9,7,-5};
Arrays.parallelPrefix(arr2,(left,right)->left*right); //根据前两个元素的值计算当前元素的值
System.out.println(Arrays.toString(arr2));
long []arr3=new long[5];
Arrays.parallelSetAll(arr3,operand->operand*5);//根据索引计算当前元素的值
System.out.println(Arrays.toString(arr3));
}
最后Lambda表达式用法千变万化,最后分享一个map里的lambda用法
//给出一个String类型的数组,找出其中各个素数,并统计其出现次数
public void primaryOccurrence(String... numbers) {
List<String> l = Arrays.asList(numbers); //把数组元素添加给表
Map<Integer, Integer> r = l.stream()
.map(e -> new Integer(e))
.filter(e -> Primes.isPrime(e))
.collect( Collectors.groupingBy(p->p, Collectors.summingInt(p->1)) );
System.out.println("primaryOccurrence result is: " + r);
}