[编程开发] java中的::是什么意思?

[复制链接]
wodesunin 发表于 2023-10-8 06:51:32|来自:北京 | 显示全部楼层 |阅读模式
比如forEach(System.out::println)代表了什么?
全部回复5 显示全部楼层
xjdata 发表于 2023-10-8 06:52:15|来自:北京 | 显示全部楼层
这题我来,顺便复习一下java的lambda表达式。
Java8 推出了属于Java的lambda表达式,与一众的 => 不同,Java选择了 -> 做为箭头符号。有没有观众知道为什么这么选择。 lambda表达式的基本格式是这样的
  1. ( )->{ }
复制代码
具体是什么意思呢,这里就要提一下使用lambda表达式的前提了。
<hr/>labmda表达式一般用来实现一个函数式接口。

比如java.lang.Iterable 的 foreach(xxx)方法中的xxx位置需要一个 Consumer 接口类
  1. default void forEach(Consumer<? super T> action) {
  2.     Objects.requireNonNull(action);
  3.     for (T t : this) {
  4.         action.accept(t);
  5.     }
  6. }
复制代码
而 Consumer 正好是一个函数式接口,@FunctionalInterface 注解了解一下。
  1. @FunctionalInterface
  2. public interface Consumer<T> {
  3.     ...
  4.     void accept(T t);
  5.     ...
  6. }
复制代码
这时候可以这么用  Consumer one = (x) -> { System.out.println(x) } ,这句可以和上面结合起来看,x就是accept中的t,花括号里没有return是因为accept返回值是void。因为 Consumer 接口中只有一个方法,因此不会有冲突的可能。 然后再把 one 传进去forEach就可以了。
<hr/>赋值后,也可以直接调用接口中的方法。相似的,我们也可以自己写一个函数式接口。
  1. interface Person{
  2.     void say(String t);
  3. }
  4. class AnimalSay{  
  5.      public static void saySomething(String something,Person person) {
  6.          person.say(something);
  7.      }
  8. }
  9. public class Mian {
  10.      public static void main(String[] args) {  
  11.          Person xiaoMing = (x) -> { System.out.println(x); };    //1
  12.          //xiaoMing.say("hello");
  13.          AnimalSay.saySomething("hello", xiaoMing );           //2
  14.          //AnimalSay.saySomething("hello", (x)->{System.out.println(x)};);    //3
  15.      }
  16. }
复制代码
可以直接把 lambda 表达式直接放在需要的位置。这时候 lambda 就可以自动变成所需类 (上面代码被注掉的部分)。
那么回到最开始也有两种用法
  1. //第一种
  2. Consumer one = (x) -> { System.out.println(x);};
  3. Iterable.foreach(one);
  4. //第二种
  5. Iterable.foreach( x->System.out.println(x) );
复制代码
<hr/>那么双冒号是什么意思?

lambda 表达式允许4种方式的双冒号


其中的 Class 指的是类名,产生于 class Class { }。object 是实例对象,产生于 Class object = new Class();  一个一个的说。
object::instanceMethod  的一个典型用法就是 System.out::println
out继承于FilterOutputStream,可以看成System的一个成员对象,通过它可以调用printstream中的输出方法。  System.out是printStream的实例: 因为System类当中的out 数据成员是由printStream流创建出来的对象,在system类中为public static final printStream out  而且out又是static的,所以只能够通过system来调用,即为System.out了。System.out说是printStream的实例化对象,意思就是说System类当中的out数据成员即为printStream 类的对象了。System.out 整个就表示屏幕输出了,这时候还只是字节流而已,当调用pringStream的方法println()是就表示用什么用的方式打印输出的关系了。
在此请回看 Consumer 类的代码,就明白了为什么 ```Consumer one = System.out::println``` 是 ```Consumer one = t -> System.out.println(t)``` 的简写

在这里插一个多线程的lambda表达式使用技巧:
  1. public class SynchronizedTest {
  2.     public synchronized void method1() {
  3.         System.out.println("Method 1 start");
  4.         try {
  5.             System.out.println("Method 1 execute");
  6.             Thread.sleep(3000);
  7.         } catch (InterruptedException e) {
  8.             e.printStackTrace();
  9.         }
  10.         System.out.println("Method 1 end");
  11.     }
  12.     public synchronized void method2() {
  13.         System.out.println("Method 2 start");
  14.         try {
  15.             System.out.println("Method 2 execute");
  16.             Thread.sleep(1000);
  17.         } catch (InterruptedException e) {
  18.             e.printStackTrace();
  19.         }
  20.         System.out.println("Method 2 end");
  21.     }
  22.     public static void main(String[] args) {
  23.         // 一种最简洁的启动方法
  24. //        new Thread( ()-> new SynchronizedTest().method1() ).start();
  25.         //新建实例对象的启动方法
  26.         final SynchronizedTest test = new SynchronizedTest();
  27.         new Thread(test::method1).start();
  28.         new Thread( ()-> test.method1() ).start();
  29.         //显式的赋值Runnable后再启动
  30.         Runnable tt = test::method1;
  31.         new Thread(tt).start();
  32.         //
  33.         new Thread(test::method2).start();
  34.         //运行完后对结果有没有疑问? 为什么2后面还有1?
  35.     }
  36. }
复制代码
xiaohongchen58 发表于 2023-10-8 06:52:48|来自:北京 | 显示全部楼层
  1. List<String> list = new ArrayList<>();
  2. list.add("a");
  3. list.add("b");
  4. list.add("c");
  5. list.forEach(System.out::println);
复制代码
其中list.forEach可以改写成以下代码:
  1. for(int i = 0; i < list.size(); i++) {
  2.     System.out.println(list.get(i));
  3. }
复制代码
或者等于以下代码:
  1. for(String s : list) {
  2.     System.out.println(s);
  3. }
复制代码
我们来看forEach方法的源码:
  1. default void forEach(Consumer<? super T> action) {
  2.     Objects.requireNonNull(action);
  3.     for (T t : this) {
  4.          action.accept(t);
  5.     }
  6. }
复制代码
其实它本质上是调用了for循环,它的参数的是一个Consumer对象,在for循环中调用了对象的accept方法,注意该方法是接口的default方法,是JDK8新增的语法。
至于为什么可以写成System.out::println,这种语法叫做方法引用。该功能特性也是JDK8以后引入的,你可以把它看做拉姆达表达式的语法糖。如果我们不这样写的话,我们可以用拉姆达表达式改写成以下代码:
  1. list.forEach((t) -> System.out.println(t));
复制代码
如果还不明白的话,也可以这样:
  1. list.forEach((String t) -> System.out.println(t));
复制代码
这样的效果跟System.out::println是一样的。
zk2008 发表于 2023-10-8 06:53:14|来自:北京 | 显示全部楼层
这里是java8 的新特性的应用。

forEach是属于java集合的一个方法,准确来说,集合在java8中拥有一个stream方法,可以得到一个流对象,这个对象拥有很多方法,这些方法可以很方便的对集合进行例如排序,分组,计数,遍历,转换等操作,而遍历是比较常见的一种,forEach就是用来做这个的,这里的forEach就是stream的forEach。
java此时还有另外一个特性叫做lambda表达式和函数式接口,仅仅有一个未实现方法的接口,可以直接写作(参数列表) -> {方法体}这种形式。
例如:
  1. @FunctionalInterface
  2. public interface FuncA {
  3.    void doSomeThing(String str);
  4. }
复制代码
那么上面这种接口就可以直接写作:
FuncA funcA = (str) -> {
  System.out.println("hello");
};
类似的还有Swing或者javaFx的监听器:
btn.addActionListener(e->{
  // do something
});
这样就省去了之前需要专为他编写一个实现类或者匿名内部类的代码,直接对接口进行实现。

而在这之上,如果一个方法的调用中,这个方法给接口提供的参数和他接收的返回,和你现有某个实现完全一致,就可以进一步进行简化,称为方法引用。

forEach方法提供一个某种类型的Object(具体是什么类型是要看Stream类的泛型参数<T>的,不过一般就是这个集合提供的那种类型),而System.out.println可以接受一个Object,因此,forEach提供的参数和System.out.println的参数类型是一致的,可以进行这种简写。

具体来说就是:原本应该写为:
.forEach(element -> {
System.out.println(element)
})
但是System.out.println的参数和传递的参数element 的类型完全匹配,所以这样的时候就可以简化为:
.forEach(System.out::println)
即forEach将会使用System.out对象的println方法进行接下来的操作。
刘德华摸周杰伦 发表于 2023-10-8 06:53:20|来自:北京 | 显示全部楼层
  1. forEach(System.out::println) ;
复制代码
等价于 :
  1. forEach((e) -> System.out.println());
复制代码
forEach 这个方法接收一个函数是接口的实现,这里是一个consumer,接收一个参数,不返回参数。
System.out::println 类似于一个语法糖,实际上等价于lambda表达式的方法。详细了解请看java 8 函数式编程的内容。
LTHlee 发表于 2023-10-8 06:54:11|来自:北京 | 显示全部楼层
Java的lambda表达式语法,基本等价于
for(xxxxx){System.out.println(xxx)}

快速回帖

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则