梳理Android开发中经常用的语法糖

本文探讨了Android开发中常见的语法糖,包括泛型的类型擦除、switch支持String和枚举的实现原理、方法变长参数的内部机制、增强for循环的使用以及Lambda表达式的概念和函数式接口的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

语法糖(Syntactic Sugar),也称糖衣语法,指在计算机语言中添加的某种语法,这种语法对语言本身功能来说没有什么影响,只是为了方便程序员的开发,提高开发效率。说白了,语法糖就是对现有语法的一个封装。

语法糖在我们Android开发中经常遇到的我暂且归纳为以下几类:

1、 泛型

泛型我们平时会经常遇到,在写一些框架的时候是必不可少的元素。但是其一个实现机制很容易被我们忽视.

类型擦除

泛型这种语法糖,编译器会在编译期间「擦除」泛型语法并相应的做出一些类型转换动作。对于java虚拟机来说,他根本不认识Map<String, String> map这样的语法。需要在编译阶段通过类型擦除的方式进行解语法糖。

类型擦除的主要过程如下:

a、将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。

b、移除所有的类型参数。

实例:

原代码
Map<String, String> map = new HashMap<String, String>();  
map.put("name", "zhufk");  
map.put("age", "28");  
解语法糖之后
Map map = new HashMap();  
map.put("name", "zhufk");  
map.put("age", "28");  
原代码
public static <A extends Comparable<A>> A max(Collection<A> xs) {
    Iterator<A> xi = xs.iterator();
    A w = xi.next();
    while (xi.hasNext()) {
        A x = xi.next();
        if (w.compareTo(x) < 0)
            w = x;
    }
    return w;
}
解语法糖之后
public static Comparable max(Collection xs){
    Iterator xi = xs.iterator();
    Comparable w = (Comparable)xi.next();
    while(xi.hasNext())
    {
        Comparable x = (Comparable)xi.next();
        if(w.compareTo(x) < 0)
            w = x;
    }
    return w;
}

总结:虚拟机中没有泛型,只有普通类和普通方法,所有泛型类的类型参数在编译时都会被擦除,泛型类并没有自己独有的Class类对象。比如并不存在List<String>.class或是List<Integer>.class,而只有List.class。

2、枚举(switch 支持 String 与枚举)

java中的swith自身原本就支持基本类型。比如int、char等。对于int类型,直接进行数值的比较。对于char类型则是比较其ascii码。所以,对于编译器来说,switch中其实只能使用整型,任何类型的比较都要转换成整型。比如byte。short,char(ackii码是整型)以及int。

那么接下来看下switch对String得支持,有以下代码:

原代码
public class switchDemoString {
    public static void main(String[] args) {
        String str = "world";
        switch (str) {
        case "hello":
            System.out.println("hello");
            break;
        case "world":
            System.out.println("world");
            break;
        default:
            break;
        }
    }
}
解语法糖之后的代码
public class switchDemoString{
    public switchDemoString(){
    }
    public static void main(String args[]){
        String str = "world";
        String s;
        switch((s = str).hashCode()){
        default:
            break;
        case 99162322:
            if(s.equals("hello"))
                System.out.println("hello");
            break;
        case 113318802:
            if(s.equals("world"))
                System.out.println("world");
            break;
        }
    }
}

看到这个代码,你知道原来字符串的switch是通过equals()和hashCode()方法来实现的。还好hashCode()方法返回的是int,而不是long。 

顺道我们来解释下枚举的基本知识

声明枚举时必须使用 enum 关键字,然后定义枚举的名称、可访问性、基础类型和成员等。

a、最基本得创建方式

public enum ColorEnum {
    RED,BLUE,GREEN
}

我们看着很熟悉,但是它内部实现的方式有注意过吗:

public final class ColorEnum extends Enum
{

    //返回存储枚举实例的数组的副本。values()方法通常用于foreach循环遍历枚举常量。
    public static ColorEnum[] values()
    {
        return (ColorEnum[])$VALUES.clone();
    }
    //根据实例名获取实例
    public static ColorEnum valueOf(String s)
    {
        return (ColorEnum)Enum.valueOf(ColorEnum, s);
    }

    //私有构造方法,这里调用了父类的构造方法,其中参数s对应了常量名,参数i代表枚举的一个顺序(这个顺序与枚举的声明顺序对应,用于oridinal()方法返回顺序值)
    private ColorEnum(String s, int i)
    {
        super(s, i);
    }

    //我们定义的枚举在这里声明了三个 ColorEnum的常量对象引用,对象的实例化在static静态块中
    public static final ColorEnum RED;
    public static final ColorEnum BLUE;
    public static final ColorEnum GREEN;
    //将所有枚举的实例存放在数组中
    private static final ColorEnum $VALUES[];

    static 
    {
        RED = new ColorEnum("RED", 0);
        BLUE = new ColorEnum("BLUE", 1);
        GREEN = new ColorEnum("GREEN", 2);
        //将所有枚举的实例存放在数组中
        $VALUES = (new ColorEnum[] {
            RED, BLUE, GREEN
        });
    }
}

 

b、增加自己的字段以及一些辅助方法

public enum ColorEnum {
    RED("red","红色"),GREEN("green","绿色"),BLUE("blue","蓝色");
    //防止字段值被修改,增加的字段也统一final表示常量
    private final String key;
    private final String value;
    
    private ColorEnum(String key,String value){
        this.key = key;
        this.value = value;
    }
    //根据key获取枚举
    public static ColorEnum getEnumByKey(String key){
        if(null == key){
            return null;
        }
        for(ColorEnum temp:ColorEnum.values()){
            if(temp.getKey().equals(key)){
                return temp;
            }
        }
        return null;
    }
    public String getKey() {
        return key;
    }
    public String getValue() {
        return value;
    }
}

内部实现同基本方式雷同我们就不一一介绍啦。

3、方法变长参数

我们在声明方法是经常会这么写:

原代码
public void printInfo(){
    print(“zhufk”,“求关注”)
}
public void print(String... strs){
    for (int i = 0; i < strs.length; i++){
        System.out.println(strs[i]);
    }
}
解语法糖之后

public void printInfo(){
{
    print(new String[] {
        "Holis", "\u516C\u4F17\u53F7"});
}

public transient void print(String strs[])
{
    for(int i = 0; i < strs.length; i++)
        System.out.println(strs[i]);

}

总结:可变参数在被使用的时候,他首先会创建一个数组,数组的长度就是调用该方法是传递的实参的个数,然后再把参数值全部放到这个数组当中,然后再把这个数组作为参数传递到被调用的方法中。

4、 for-each

增强for循环(for-each)相信大家都不陌生,日常开发经常会用到的,他会比for循环要少写很多代码,那么这个语法糖背后是如何实现的呢?

原代码
public static void main(String... args) {
    String[] strs = {"zhufk", "QQ:308585736",“求关注”};
    for (String s : strs) {
        System.out.println(s);
    }
    List<String> strList = ImmutableList.of("zhufk", "QQ:308585736",“求关注”);
    for (String s : strList) {
        System.out.println(s);
    }
}
解语法糖之后
public static transient void main(String args[])
{
    String strs[] = {
     "zhufk", "\u516C\u4F17308585736", "\u535A\u5BA2\uFF1A"
    };
    String args1[] = strs;
    int i = args1.length;
    for(int j = 0; j < i; j++)
    {
        String s = args1[j];
        System.out.println(s);
    }

    List strList =ImmutableList.of("zhufk","\u516C\u4F17308585736","\u535A\u5BA2\uFF1A");
    String s;
    for(Iterator iterator = strList.iterator(); iterator.hasNext(); System.out.println(s))
        s = (String)iterator.next();

}

for-each的实现原理其实就是使用了普通的for循环和迭代器。

5、Lambda表达式

我们在显示开发中如果用到过RxJava开发,应该很熟悉这种语法

首先我们先来了解一下Lambda:

什么叫函数式接口?他和普通接口有什么区别? 
“函数式接口”是指仅仅只包含一个抽象方法的接口(可以包含默认方法和静态方法),其他特征和普通接口没有任何区别,Java中Runnalbe,Callable等就是个函数式接口;。我们可以给一个符合函数式接口添加@FunctionalInterface注解,这样就显式的指明该接口是一个函数式接口,如果不是,编译器会直接提示错误。当然你也可以不用添加此注解。添加的好处在于,由于Lambda表达式只支持函数式接口,如果恰好这个接口被应用于Lambda表达式,某天你手抖不小心添加了个抽象方法,编译器会提示错误。

//显式指明该接口是函数式接口
@FunctionalInterface
public interface ActionListener{

    void actionPerformed(ActionEvent e);//这是一个抽象方法

    default String onDefalutMethod(){//这是一个默认方法 
        return "这是一个默认方法";
    }

    static String onStaticMethod(){
        return "这是一个静态方法";
    }

}

通常的调用方式
button.addActionListener(new ActionListener(){ 

    public void actionPerformed(ActionEvent e) {

    ShowDialog(e.tostring());

    }

})

用Lambda表达式
button.addActionListener((ActionEvent e) -> ShowDialog(e.tostring()));


待续。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值