就来说一说这个匿名内部类,用Java做例子,后面插一点c#。这个东西我之前也知道了很久,一直没有认真去看过,这次就来看看吧,今天的代码量相对很多。
匿名内部类
这里就说匿名内部类,关于其他内部类老规矩,以后有空了再说
匿名内部类的使用
好像很多地方都不说这具体是个什么东西,都是直接上代码说话。
顾名思义,是没有名字的内部类,举个栗子
new View.OnClickListener(){
@Override
public void onClick(View v) {
}
};
这就是一个典型的常用的内部类,在对按钮等进行事件监听的时候会用到这个,但是明显一看很容易发现这是一个接口,对接口进行new是错误的。
下面来还原一个完整的匿名内部类应该就很清楚了
abstract class Person {
public abstract void eat();
}
class Child extends Person {
public void eat() {
System.out.println("eat something");
}
}
public class Demo {
public static void main(String[] args) {
Person p = new Child();
p.eat();
}
}
Person是一个抽象类,Child继承这个类成为一个子类,main函数里实例化这个类,其中我们需要在匿名内部类中隐藏Child这个类,如下所示
abstract class Person {
public abstract void eat();
}
public class Demo {
public static void main(String[] args) {
Person p = new Person() {
public void eat() {
System.out.println("eat something");
}
};
p.eat();
}
}
我们可以看到,这样相当于在实例化的时候直接重写了Child这个类,这就是匿名内部类。
匿名内部类没有名字,创建格式如下
new 父类构造器(参数列表)|实现接口()
{
//匿名内部类的类体部分
}
必须继承一个父类或者实现一个接口,没有class关键字,直接使用new生成一个对象的引用。再如下所示
public abstract class Bird {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract int fly();
}
public class Test {
public void test(Bird bird){
System.out.println(bird.getName() + "能够飞 " + bird.fly() + "米");
}
public static void main(String[] args) {
Test test = new Test();
test.test(new Bird() {
public int fly() {
return 10000;
}
public String getName() {
return "大雁";
}
});
}
}
------------------
Output:
大雁能够飞 10000米
test方法接收一个Bird类型参数,但是我们都知道一个抽象类是不能进行实例化的,也就是直接new,所以我们必须要有一个实现类才可以进行new操作,但是由于它是一个抽象类,所以通过匿名内部类创建一个Bird实例。又因为匿名内部类不能是抽象类,所以我们必须要实现抽象父类或者接口里的所有抽象方法才行。
匿名内部类的注意事项
- 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
- 匿名内部类中是不能定义构造函数的。
- 匿名内部类中不能存在任何的静态成员变量和静态方法。
- 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
- 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
匿名内部类的初始化
它没有构造函数,name怎么进行相应初始化呢?
使用构造代码块进行初始化,可以达到一个构造器的效果。
public class OutClass {
public InnerClass getInnerClass(final int age,final String name){
return new InnerClass() {
int age_ ;
String name_;
//构造代码块完成初始化工作
{
if(0 < age && age < 200){
age_ = age;
name_ = name;
}
}
public String getName() {
return name_;
}
public int getAge() {
return age_;
}
};
}
public static void main(String[] args) {
OutClass out = new OutClass();
InnerClass inner_1 = out.getInnerClass(201, "chenssy");
System.out.println(inner_1.getName());
InnerClass inner_2 = out.getInnerClass(23, "chenssy");
System.out.println(inner_2.getName());
}
}
既然说到了这里,顺便说一说代码块
代码块就是用{}包起来的代码,进行封装,形成一个独立的数据体,用于实现特定的算法。代码块不能单独运行,必须要有一个运行主体。
Java的代码块主要分为四种
- 普通代码块
普通代码块是不能够单独存在的,它必须要紧跟在方法名后面。同时也必须要使用方法名调用它。
public class Test {
public void test(){
System.out.println("普通代码块");
}
}
- 静态代码块
想到静态我们就会想到static,静态代码块就是用static修饰的用{}括起来的代码段,它的主要目的就是对静态属性进行初始化。
public class Test {
static{
System.out.println("静态代码块");
}
}
- 同步代码块
使用 synchronized 关键字修饰,并使用“{}”括起来的代码片段,它表示同一时间只能有一个线程进入到该方法块中,是一种多线程保护机制。 - 构造代码块
在类中直接定义没有任何修饰符、前缀、后缀的代码块即为构造代码块。我们明白一个类必须至少有一个构造函数,构造函数在生成对象时被调用。构造代码块和构造函数一样同样是在生成一个对象时被调用。
public class Test {
/**
* 构造代码
*/
{
System.out.println("执行构造代码块...");
}
/**
* 无参构造函数
*/
public Test(){
System.out.println("执行无参构造函数...");
}
/**
* 有参构造函数
* @param id id
*/
public Test(String id){
System.out.println("执行有参构造函数...");
}
}
上面定义了一个非常简单的类,该类包含无参构造函数、有参构造函数以及构造代码块,同时在上面也提过代码块是没有独立运行的能力,他必须要有一个可以承载的载体,那么编译器会如何来处理构造代码块呢?编译器会将代码块按照他们的顺序(假如有多个代码块)插入到所有的构造函数的最前端,这样就能保证不管调用哪个构造函数都会执行所有的构造代码块。上面代码等同于如下形式:
public class Test {
/**
* 无参构造函数
*/
public Test(){
System.out.println("执行构造代码块...");
System.out.println("执行无参构造函数...");
}
/**
* 有参构造函数
* @param id id
*/
public Test(String id){
System.out.println("执行构造代码块...");
System.out.println("执行有参构造函数...");
}
}
运行结果如下
public static void main(String[] args) {
new Test();
System.out.println("----------------");
new Test("1");
}
------------
Output:
执行构造代码块...
执行无参构造函数...
----------------
执行构造代码块...
执行有参构造函数...
从上面的运行结果可以看出在new一个对象的时候总是先执行构造代码,再执行构造函数,但是有一点需要注意构造代码不是在构造函数之前运行的,它是依托构造函数执行的。
各个代码块执行顺序为:静态代码块 > 构造代码块 > 构造函数。
声明
本部分转自:点击这里
作者:chenssy
出处:http://www.cnblogs.com/chenssy/
匿名内部类就说到这吧,下面是我想看的lambda表达式。
lambda表达式
先划重点,写到这里我才意识到lambda是Java8的新特性,我抱着我的Java6不说话
“Lambda 表达式”(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包(注意和数学传统意义上的不同)。
Java8发布已经有一段时间了,这次发布的改动比较大,很多人将这次改动与Java5的升级相提并论。Java8其中一个很重要的新特性就是lambda表达式,允许我们将行为传到函数中。想想看,在Java8之前我们想要将行为传入函数,仅有的选择就是匿名内部类。Java8发布以后,lambda表达式将大量替代匿名内部类的使用,简化代码的同时,更突出了原来匿名内部类中最重要的那部分包含真正逻辑的代码。
下面看看一些常用写法
替代匿名内部类
毫无疑问,lambda表达式用得最多的场合就是替代匿名内部类,而实现Runnable接口是匿名内部类的经典例子。lambda表达式的功能相当强大,用()->就可以代替整个匿名内部类!
这是我们上面刚说的匿名内部类
public void oldRunable() {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("The old runable now is using!");
}
}).start();
}
下面我们看看怎么用表达式写出来
public void runable() {
new Thread(() -> System.out.println("It's a lambda function!")).start();
}
输出结果
The old runable now is using!
It's a lambda function!
使用lambda表达式对集合进行迭代
Java的集合类是日常开发中经常用到的,甚至说没有哪个java代码中没有使用到集合类…而对集合类最常见的操作就是进行迭代遍历了。
虽然Java里没用过foreach,但是我在c#里提过。
public void iterTest() {
List<String> languages = Arrays.asList("java","scala","python");
//before java8
for(String each:languages) {
System.out.println(each);
}
//after java8
languages.forEach(x -> System.out.println(x));
languages.forEach(System.out::println);
}
用lambda表达式实现map
至于什么是map可以百度以下。
一提到函数式编程,一提到lambda表达式,怎么能不提map…没错,java8肯定也是支持的。
public void mapTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0);
cost.stream().map(x -> x + x*0.05).forEach(x -> System.out.println(x));
}
输出结果
10.5
21.0
31.5
map函数可以说是函数式编程里最重要的一个方法了。map的作用是将一个对象变换为另外一个。在我们的例子中,就是通过map方法将cost增加了0.05倍的大小然后输出。
用lambda表达式实现map与reduce
既然提到了map,又怎能不提到reduce。reduce与map一样,也是函数式编程里最重要的几个方法之一…map的作用是将一个对象变为另外一个,而reduce实现的则是将所有值合并为一个。
public void mapReduceTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0);
double allCost = cost.stream().map(x -> x+x*0.05).reduce((sum,x) -> sum + x).get();
System.out.println(allCost);
}
输出结果
63.0
filter操作
filter也是我们经常使用的一个操作。在操作集合的时候,经常需要从原始的集合中过滤掉一部分元素。
public void filterTest() {
List<Double> cost = Arrays.asList(10.0, 20.0,30.0,40.0);
List<Double> filteredCost = cost.stream().filter(x -> x > 25.0).collect(Collectors.toList());
filteredCost.forEach(x -> System.out.println(x));
}
输出结果
30.0
40.0
与函数式接口Predicate配合
这个看不太懂,留着慢慢看。
除了在语言层面支持函数式编程风格,Java 8也添加了一个包,叫做 java.util.function。它包含了很多类,用来支持Java的函数式编程。其中一个便是Predicate,使用 java.util.function.Predicate 函数式接口以及lambda表达式,可以向API方法添加逻辑,用更少的代码支持更多的动态行为。Predicate接口非常适用于做过滤。
public static void filterTest(List<String> languages, Predicate<String> condition) {
languages.stream().filter(x -> condition.test(x)).forEach(x -> System.out.println(x + " "));
}
public static void main(String[] args) {
List<String> languages = Arrays.asList("Java","Python","scala","Shell","R");
System.out.println("Language starts with J: ");
filterTest(languages,x -> x.startsWith("J"));
System.out.println("\nLanguage ends with a: ");
filterTest(languages,x -> x.endsWith("a"));
System.out.println("\nAll languages: ");
filterTest(languages,x -> true);
System.out.println("\nNo languages: ");
filterTest(languages,x -> false);
System.out.println("\nLanguage length bigger three: ");
filterTest(languages,x -> x.length() > 4);
}
运行结果
Language starts with J:
Java
Language ends with a:
Java
scala
All languages:
Java
Python
scala
Shell
R
No languages:
Language length bigger three:
Python
scala
Shell
可以看到,Stream API的过滤方法也接受一个Predicate,这意味着可以将我们定制的 filter() 方法替换成写在里面的内联代码,这也是lambda表达式的魔力。
声明
本部分转自:java8 手把手教你学会写lambda表达式
C#的匿名方法和lambda表达式初步
顺便提一下C#,因为我大致只用俩语言。
微软告诉我们:我们在C#2.0之前就有委托了,在2.0之后又引入了匿名方法,C#3.0之后,又引入了Lambda表达式,他们三者之间的顺序是:委托->匿名表达式->Lambda表达式,微软的一步步升级,带给我们编程上的优美,简洁,可读性强…
委托和匿名表达式以及lambda表达式
delegate int calculator(int x, int y); //委托类型
static void Main()
{
calculator cal = new calculator(Adding);
int He = cal(1, 1);
Console.Write(He);
}
/// <summary>
/// 加法
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
public static int Adding(int x, int y)
{
return x + y;
}
delegate int calculator(int x, int y); //委托
static void Main()
{
calculator cal = delegate(int num1,int num2)
{
return num1 + num2;
};
int he = cal(1, 1);
Console.Write(he);
}
关于委托,我之前写过一个c#委托小技巧的文章,点击此处跳转。
关于lambda表达式,上述栗子可以写成
delegate int calculator(int x, int y); //委托类型
static void Main()
{
calculator cal = (x, y) => x + y;//Lambda表达式,大家发现没有,代码一个比一个简洁
int he = cal(1, 1);
Console.Write(he);
}
若要创建 Lambda 表达式,需要在 Lambda 运算符 => 左侧指定输入参数(如果有),然后在另一侧输入表达式或语句块。 例如,lambda 表达式 x => x * x 指定名为 x 的参数并返回 x 的平方值。
“Lambda表达式"是一个特殊的匿名函数,是一种高效的类似于函数式编程的表达式,Lambda简化了开发中需要编写的代码量。它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带有可绑定到委托或表达式树的输入参数的内联表达式。所有Lambda表达式都使用Lambda运算符=>,该运算符读作"goes to”。Lambda运算符的左边是输入参数(如果有),右边是表达式或语句块。Lambda表达式x => x * x读作"x goes to x times x"。
下面是几个简单的例子
delegate bool MyBol(int x, int y);
delegate bool MyBol_2(int x, string y);
delegate int calculator(int x, int y); //委托类型
delegate void VS();
static void Main()
{
MyBol Bol = (x, y) => x == y;
MyBol_2 Bol_2 = (x, s) => s.Length > x;
calculator C = (X, Y) => X * Y;
VS S = () => Console.Write("我是无参数Labada表达式");
//
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count(n => n % 2 == 1);
//
List<People> people = LoadData();//初始化
IEnumerable<People> results = people.Where(delegate(People p) { return p.age > 20; });
}
private static List<People> LoadData()
{
List<People> people = new List<People>(); //创建泛型对象
People p1 = new People(21, "guojing"); //创建一个对象
People p2 = new People(21, "wujunmin"); //创建一个对象
People p3 = new People(20, "muqing"); //创建一个对象
People p4 = new People(23, "lupan"); //创建一个对象
people.Add(p1); //添加一个对象
people.Add(p2); //添加一个对象
people.Add(p3); //添加一个对象
people.Add(p4);
return people;
}
}
public class People
{
public int age { get; set; } //设置属性
public string name { get; set; } //设置属性
public People(int age, string name) //设置属性(构造函数构造)
{
this.age = age; //初始化属性值age
this.name = name; //初始化属性值name
}
}
Func<T>委托
T 是参数类型,这是一个泛型类型的委托,用起来很方便。
static void Main(string[] args)
{
Func<int, string> gwl = p => p + 10 + "--返回类型为string";
Console.WriteLine(gwl(10) + ""); //打印‘20--返回类型为string’,z对应参数b,p对应参数a
Console.ReadKey();
}
我们可以看到,这里的p为int 类型参数, 然而lambda主体返回的是string类型的。
static void Main(string[] args)
{
Func<int, int, bool> gwl = (p, j) =>
{
if (p + j == 10)
{
return true;
}
return false;
};
Console.WriteLine(gwl(5,5) + ""); //打印‘True’,z对应参数b,p对应参数a
Console.ReadKey();
}
从这个栗子,我们能看到,p为int类型,j为int类型,返回值为bool类型。
声明
本部分转自:C# Lambda表达式