资深架构师和你谈谈 Kotlin 的泛型

SpringBoot框架面试专题(初级-中级)-第五节 在上面的示例中,我们加载了一个名为file.txt的文件,并将其转换为InputStreamResource类型,然后将其作为响应体返回给客户端。在Spring Boot中,可以使用Spring框架提供的AOP(面向切面编程)机制来实现切面编程,通过AOP可以在不修改原有代码的情况下,对系统进行横向扩展,例如添加日志、性能监控、事务管理等功能。在上面的示例中,我们通过SendGrid提供的Java SDK来发送邮件,先创建一个Mail对象,并设置相关的邮件参数,然后使用SendGrid的API来发送邮件。 阅读详情

引子

相信总是有很多人,总是在抱怨泛型无论怎么学习,都只是停留在一个简单使用的水平,所以一直为此而备受苦恼。

Kotlin 作为一门能和 Java 相互调用的语言,自然也支持泛型,不过 Kotlin 的新关键字 in 和 out 却总能绕晕一部分人,归根结底,还是因为 Java 的泛型基本功没有足够扎实。

很多人总是会产生这些疑问:

  • Kotlin 泛型和 Java 泛型到底有何区别?
  • Java 泛型存在的意义到底是什么?
  • Java 的类型擦除到底是指什么?
  • Java 泛型的上界、下界、通配符到底有何区别?它们可以实现多重限制么?
  • Java 的 <? extends T>、<? super T>、<?> 到底对应了什么?有哪些使用场景?
  • Kotlin 的 in、out、*、where 到底有何魔力?
  • 泛型方法又是什么?

今天,就用一篇文章为大家解除上述疑惑。

泛型:类型安全的利刃

众所周知,Java 在 1.5 之前,是没有泛型这个概念的。那时候的 List 还只是一个可以装下一切的集合。所以我们难免会写上这样的代码:

List list = new ArrayList();
list.add(1);
list.add("nanchen2251");
String str = (String) list.get(0);

上面的代码编译并没有任何问题,但运行的时候一定会出现常见的 ClassCastException 异常:

Exception in thread "main" java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String

这个体验非常糟糕,我们真正需要的是在代码编译的时候就能发现错误,而不是让错误的代码发布到生产环境中。

而如果上述代码我们增加上泛型,就会在编译期就能看到明显的错误啦。

List<String> list = new ArrayList<>();
list.add(1);
//  报错 Required type:String but Provided:int
list.add("nanchen2251");
String str = list.get(0);

很明显,泛型的出现,让类型更加安全,使我们在使用 List、Map 等不再需要去专门编写 StringList、StringMap 了,只需要在声明 List 的同时指定参数类型即可。

总的来说,泛型具备以下优势:

  • 类型检查,能在编译时就帮开发检查出错误;
  • 更加语义化,比如我们声明一个 LIst<String>,我们可以很直接知道里面存储的是 String 对象;
  • 能自动进行类型转换,获取数据的时候不需要再做强转操作;
  • 能写出更加通用化的代码。

类型擦除

可能有些同学思考过这样一个问题,既然泛型是和类型相关的,那么是不是也能使用类型的多态呢?

我们知道,一个子类型是可以赋值给父类型的,比如:

Object obj = "nanchen2251";
//  这是多态

Object 作为 String 的父类,自然可以接受 String 对象的赋值,这样的代码我们早已司空见惯,并没有什么问题。

但当我们写下这串代码:

List<String> list = new ArrayList<String>();
List<Object> objects = list;  
//  多态用在这里会报错 Required type:List<Object> Provided: List<String>

上面发生了赋值错误,这是因为 Java 的泛型本身具有「不可变性 Invariance」,Java 里面认为 List<String> 和 List<Object> 类型并不一致,也就是说,子类的泛型 List<String> 不属于泛型 List<Object> 的子类。

由于 Java 的泛型本身是一种 「伪泛型」,Java 为了兼容 1.5 以前的版本,不得以在泛型底层实现上使用 Object 引用,所以我们声明的泛型在编译时会发生「类型擦除」,泛型类型会被 Object 类型取代。比如:

class Demo<T> {
    void func(T t){
        // ...
    }
}

会被编译成:

class Demo {
    void func(Object t){
        // ...     
    }
}

可能你会好奇,在编译时发生类型擦除后,我们的泛型都被更换成了 Object,那为什么我们在使用的时候,却不需要强转操作呢?比如:

List<String> list = new ArrayList<>();
list.add("nanchen2251");
String str = list.get(0);
//  这里并没有要求我们把 list.get(0) 强转为 String

这是因为编译器会根据我们声明的泛型类型进行提前的类型检查,然后再进行类型擦除,擦除为 Object,但在字节码中其实还存储了我们的泛型的类型信息,在使用到泛型类型的时候会把擦除后的 Object 自动做类型强转操作。所以上面的 list.get(0) 本身就是一个经过强转的 String 对象了。

这个技术看起来还蛮好的,但却有一个弊端。就是既然擦成 Object 了,那么在运行的时候,你根本不能确定这个对象到底是什么类型,虽然你可以通过编译器帮你插入的 checkcast 来获得此对象的类型。但是你并不能把 T 真正的当作一个类型使用:比如这条语句在 Java 中是非法的。

T a = new T();
//  报错:Type parameter 'T' cannot be instantiated directly

同理,因为都被擦成了 Object,你就不能根据类型来做某种区分。

比如 instanceof:

if("nanchen2251" instanceof T.class){
                           //  报错:Identifier expected Unexpected token        
}

比如重载:

void func(T t){
//  报错:'func(T)' clashes with 'func(E)'; both methods have same erasure       
}
void func(E e){
}

同样,因为基本数据类型不属于 oop,所以也不能被擦除为 Object,所以 Java 的泛型也不能用于基本类型:

List<int> list;
//  报错:Type argument cannot be of primitive type 

oop:面向对象的程序设计(Object Oriented Programming)

到这里,是不是可以回答上面的第 3 个问题了:Java 的类型擦除到底是指什么?

首先你要明白一点,一个对象的类型永远不会被擦出的,比如你用一个 Object 去引用一个 Apple 对象,你还是可以获得到它的类型的。比如用 RTTI。

RTTI:运行时类型信息,运行时类型识别 (Run Time Type Identification)

Object object = new Apple();
System.out.println(object.getClass().getName());
//  will print Apple

哪怕它是放到泛型里的。

class FruitShop<T>{
    private T t;

    public void set(T t){
        this.t = t;
    }
    
    public  void showFruitName(){
        System.out.println(t.getClass().getName());
    }
}
FruitShop<Apple> appleShop = new FruitShop<Apple>();
appleShop.set(new Apple());
appleShop.showFruitName();
//  will print Apple too

为啥?因为引用就是一个用来访问对象的标签而已,对象一直在堆上放着呢。

所以不要断章取义认为类型擦除就是把容器内对象的类型擦掉了,所谓的类型擦除,是指容器类FruitShop<Apple>,对于 Apple 的类型声明在编译期的类型检查之后被擦掉,变为和 FruitShop<Object> 等同效果,也可以说是 FruitShop<Apple> 和 FruitShop<Banana> 被擦为和 FruitShop<Object> 等价,而不是指里面的对象本身的类型被擦掉!

那,Kotlin 中有类型擦除么?

C# 和 Java 在一开始都是不支持泛型的。Java 在 1.5 开始才加入了泛型。为了让一个不支持泛型的语言支持泛型,只有两条路可以走:

  • 以前的非泛型容器保持不变,然后平行的增加一套泛型化的类型。
  • 直接把已有的非泛型容器扩展为泛型,不添加任何新的泛型版本。

Java 由于 1.5 之前市面上一句有大量的代码,所以不得以选择了第 2 种方式,而 C# 比较机智就选择了第一种。

而 Kotlin 本身就是基于 Java 1.6 编写的,一开始就有泛型,不存在兼容老版本代码的问题,那 Kotlin 实现的泛型还具备类型擦除么?

当然具备。上面其实已经说的很清楚了,Kotlin 本身就是基于 Java 1.6 编写的,而且 Kotlin 和 Java 有极强的互调能力,当然也存在类型擦除。

不过...

你还是会发现有意思的点:

val list = ArrayList()
//  报错:Not enough information to infer type variable E

在 Java 中,不指定泛型类型是没问题的,但 Kotlin 这样不好使了。想来也简单,毕竟在 Java 1.5 之前是肯定不存在上述类似代码的,而泛型的设计初衷就不是用来装默认的 Kotlin Any 的。

泛型的上界通配符

前面说到:因为 Java 的泛型本身具有「不可变性 Invariance」,所以即使 Fruit 类是 Apple 类的父类,但 Java 里面认为 List<Fruit> 和 List<Apple> 类型并不一致,也就是说,子类的泛型 List<Apple> 不属于泛型 List<Fruit> 的子类。

所以这样的代码并不被运行。

List<Apple> apples = new ArrayList<Apple>();
List<Fruit> fruits = apples;  
//  多态用在这里会报错 Required type:List<Fruit> Provided: List<Apple>

那假如我们想突破这层限制,怎么办?使用上界通配符 ? extends。

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
    // 使用上界通配符后,编译不再报错

「上界通配符」,可以使 Java 泛型具有「协变性 Covariance」,协变就是允许上面的赋值是合法的。

在继承关系树中,子类继承自父类,可以认为父类在上,子类在下。extends 限制了泛型类型的父类型,所以叫上界。

它有两层意思:

  • 其中 ? 是个通配符,表示这个 List 的泛型类型是一个未知类型。
  • extends 限制了这个未知类型的上界,也就是泛型类型必须满足这个 extends 的限制条件,这里和定义 class 的 extends 关键字有点不一样: 它的范围不仅是所有直接和间接子类,还包括上界定义的父类本身,也就是 Fruit。 它还有 implements 的意思,即这里的上界也可以是 interface。

这个突破限制有意义么?

有的有的。

假如我们有一个接口 Fruit:

interface Fruit {
    float getWeight();
}

有两个水果类实现了 Fruit 接口:

class Banana implements Fruit {
    @Override
    public float getWeight() {
        return 0.5f;
    }
}

class Apple implements Fruit {
    @Override
    public float getWeight() {
        return 1f;
    }
}

假设我们有个需求是需要给水果称重:

List<Apple> apples = new ArrayList<>();
apples.add(new Apple());
float totalWeight = getTotalWeight(apples); 
                                //  报错:Required type: List<Fruit> Provided: List<Apple>

private float getTotalWeight(List<Fruit> fruitList) {
        float totalWeight = 0;
        for (Fruit fruit : fruitList) {
            totalWeight += fruit.getWeight();
        }
        return totalWeight;
    }

想来这也是一个非常正常的需求,秤可以称各种水果的重量,但也可以只称苹果。你不能因为我只买苹果就不给我称重吧。所以把上面的代码加上上界通配符就可以啦。

List<Apple> apples = new ArrayList<>();
apples.add(new Apple());
float totalWeight = getTotalWeight(apples); 
                                //  不再报错
                                //  增加了上界通配符 ? extends
private float getTotalWeight(List<? extends Fruit> fruitList) {
        float totalWeight = 0;
        for (Fruit fruit : fruitList) {
            totalWeight += fruit.getWeight();
        }
        return totalWeight;
    }

不过,上面使用 ? extends 上界通配符突破了一层限制,却被施加了另一层限制:只可输出不可输入。

什么意思呢?

比如:

List<Apple> apples = new ArrayList<Apple>();
List<? extends Fruit> fruits = apples;
Fruit fruit = fruits.get(0);
fruits.add(new Apple());
            //  报错:Required type: capture of ? extends Fruit Provided: Apple

声明了上界通配符泛型的集合,不再允许 add 新的对象,Apple 不行,Fruit 也不行。拓展开来说:不止是集合,自己编写一个泛型做输入也不行。

interface Shop<T> {
    void showFruitName(T t);
    T getFruit();
}

Shop<? extends Fruit> apples = new Shop<Apple>(){
    @Override
    public void showFruitName(Apple apple) { }

    @Override
    public Apple getFruit() {
        return null;
    }
};
apples.getFruit();
apples.showFruitName(new Apple());
                     // 报错:Required type: capture of ? extends Fruit Provided: Apple

泛型的下界通配符

泛型有上界通配符,那有没有下界通配符呢?

有的有的。

与上界通配符 ? extends 对应的就是下界通配符 ? super

下界通配符 ? super 所有情况和 ? extends 上界通配符刚刚相反:

  • 通配符 ? 表示 List 的泛型类型是一个 未知类型。
  • super 限制了这个未知类型的下界,也就是泛型类型必须满足这个 super 的限制条件 它的范围不仅是所有直接和间接子父类,还包括下界定义的子类本身。 super 同样支持 interface。

它被施加的新限制是:只可输入不可输出。

Shop<? super Apple> apples = new Shop<Fruit>(){
    @Override
    public void showFruitName(Fruit apple) { }

    @Override
    public Fruit getFruit() {
        return null;
    }
};
apples.showFruitName(new Apple());
Apple apple = apples.getFruit();
 //  报错:Required type: Apple Provided: capture of ? super Apple

解释下,首先 ? 表示未知类型,编译器是不确定它的类型的。

虽然不知道它的具体类型,不过在 Java 里任何对象都是 Object 的子类,所以这里只能把apples.getFruit() 获取出来的对象赋值给 Object。由于类型未知,所以直接赋值给一个 Apple 对象肯定是不负责任的,需要我们做一层强制转换,不过强制转换本身可能发生错误。

而 Apple 对象一定是这个未知类型的子类型,根据多态的特性,这里通过 showFruitName 输入 Button 对象是合法的。

小结下,Java 的泛型本身是不支持协变和逆变的:

  • 可以使用泛型通配符 ? extends 来使泛型支持协变,但是「只能读取不能修改」,这里的修改仅指对泛型集合添加元素,如果是 remove(int index) 以及 clear 当然是可以的。
  • 可以使用泛型通配符 ? super 来使泛型支持逆变,但是「只能修改不能读取」,这里说的不能读取是指不能按照泛型类型读取,你如果按照 Object 读出来再强转当然也是可以的。

理解了 Java 的泛型之后,再理解 Kotlin 中的泛型,就比较容易了。

Kotlin 的 out 和 in

和 Java 泛型一样,Kolin 中的泛型本身也是不可变的。

不过换了一种表现形式:

  • 使用关键字 out 来支持协变,等同于 Java 中的上界通配符 ? extends。
  • 使用关键字 in 来支持逆变,等同于 Java 中的下界通配符 ? super。
val appleShop: Shop<out Fruit>
val fruitShop: Shop<in Apple>

它们完全等价于:

Shop<? extends Fruit> appleShop;
Shop<? super Apple> fruitShop;

换了个写法,但作用是完全一样的。out 表示,我这个变量或者参数只用来输出,不用来输入,你只能读我不能写我;in 就反过来,表示它只用来输入,不用来输出,你只能写我不能读我。

泛型的上下界约束

上面讲的都是在使用的时候再对泛型进行限制,我们称之为「上界通配符」和「下界通配符」。那我们可以在函数设计的时候,就设置这个限制么?

可以的可以的。

比如:

open class Animal
class PetShop<T : Animal?>(val t: T)

等同于 Java 的:

class PetShop<T extends Animal> {
    private T t;

    PetShop(T t) {
        this.t = t;
    }
}

这样,我们在设计宠物店类 PetShop 就给支持的泛型设置了上界约束,支持的泛型类型必须是 Animal 的之类。所以我们使用的话:

class Cat : Animal()

val catShop = PetShop(Cat())
val appleShop = PetShop(Apple())
                      //  报错:Type mismatch. Required: Animal? Found: Apple

很明显,Apple 并不是 Animal 的子类,当然不满足 PetShop 泛型类型的上界约束。

那....可以设置多个上界约束么?

当然可以,在 Java 中,给一个泛型参数声明多个约束的方式是,使用 &:

class PetShop<T extends Animal & Serializable> {
                      //  通过 & 实现了两个上界,必须是 Animal 和 Serializable 的子类或实现类
    private T t;

    PetShop(T t) {
        this.t = t;
    }
}

而在 Kotlin 中舍弃了 & 这种方式,而是增加了 where 关键字:

open class Animal
class PetShop<T>(val t: T) where  T : Animal?, T : Serializable

通过上面的方式,就实现了多个上界的约束。

Kotlin 的通配符 *

前面我们说的泛型类型都是在我们需要知道参数类型是什么类型的,那如果我们对泛型参数的类型不感兴趣,有没有一种方式处理这个情况呢?

有的有的。

在 Kotlin 中,可以用通配符 * 来替代泛型参数。比如:

val list: MutableList<*> = mutableListOf(1, "nanchen2251")
list.add("nanchen2251")
      //  报错:Type mismatch. Required: Nothing Found: String

这个报错确实让人匪夷所思,上面用通配符代表了 MutableList 的泛型参数类型。初始化里面也加入了 String 类型,但在新 add 字符串的时候,却发生了编译错误。

而如果是这样的代码:

val list: MutableList<Any> = mutableListOf(1, "nanchen2251")
list.add("nanchen2251")
      //   不再报错

看来,所谓的通配符作为泛型参数并不等价于 Any 作为泛型参数。MutableList<*> 和 MutableList<Any> 并不是同一种列表,后者的类型是确定的,而前者的类型并不确定,编译器并不能知道这是一种什么类型。所以它不被允许添加元素,因为会导致类型不安全。

不过细心的同学肯定发现了,这个和前面泛型的协变非常类似。其实通配符 * 不过是一种语法糖,背后也是用协变来实现的。所以:MutableList<*> 等价于 MutableList<out Any?>,使用通配符与协变有着一样的特性。

在 Java 中,也有一样意义的通配符,不过使用的是 ? 作为通配。

List<?> list = new ArrayList<Apple>(); 

Java 中的通配符 ? 也等价于 ? extends Object。

多个泛型参数声明

那可以声明多个泛型么?

可以的可以的。

HashMap 不就是一个典型的例子么?

class HashMap<K,V>

多个泛型,可以通过 , 进行分割,多个声明,上面是两个,实际上多个都是可以的。

class HashMap<K: Animal, V, T, M, Z : Serializable>

泛型方法

上面讲的都是都是在类上声明泛型类型,那可以声明在方法上么?

可以的可以的。

如果你是一名 Android 开发,View 的 findViewById 不就是最好的例子么?

public final <T extends View> T findViewById(@IdRes int id) {
    if (id == NO_ID) {
        return null;
    }
    return findViewTraversal(id);
}

很明显,View 是没有泛型参数类型的,但其 findViewById 就是典型的泛型方法,泛型声明就在方法上。

上述写法改写成 Kotlin 也非常简单:

fun <T : View?> findViewById(@IdRes id: Int): T? {
    return if (id == View.NO_ID) {
        null
    } else findViewTraversal(id)
}

Kotlin 的 reified

前面有说到,由于 Java 中的泛型存在类型擦除的情况,任何在运行时需要知道泛型确切类型信息的操作都没法用了。比如你不能检查一个对象是否为泛型类型 T 的实例:

<T> void printIfTypeMatch(Object item) {
    if (item instanceof T) { //  IDE 会提示错误,illegal generic type for instanceof

    }
}

Kotlin 里同样也不行:

fun <T> printIfTypeMatch(item: Any) {
    if (item is T) { //  IDE 会提示错误,Cannot check for instance of erased type: T
        println(item)
    }
}

这个问题,在 Java 中的解决方案通常是额外传递一个 Class<T> 类型的参数,然后通过 Class#isInstance 方法来检查:

                               
<T> void check(Object item, Class<T> type) {
    if (type.isInstance(item)) {
               
    }
}

Kotlin 中同样可以这么解决,不过还有一个更方便的做法:使用关键字 reified 配合 inline 来解决:

            
inline fun <reified T> printIfTypeMatch(item: Any) {
    if (item is T) { //  这里就不会在提示错误了

    }
}

上面的 Gson 解析的时候用的非常广泛,比如咱们项目里就有这样的扩展方法:

inline fun <reified T> String?.toObject(type: Type? = null): T? {
    return if (type != null) {
        GsonFactory.GSON.fromJson(this, type)
    } else {
        GsonFactory.GSON.fromJson(this, T::class.java)
    }
}

总结

本文花了非常大的篇幅来讲 Kotlin 的泛型和 Java 的泛型,现在再回过头去回答文首的几个问题,同学你有谱了吗?如果还是感觉一知半解,不妨多看几遍。

pytorch中dataloader的num_workers参数 结论速递 在Windows系统中,num_workers参数建议设为0,在Linux系统则不需担心。 1 问题描述 在之前的任务超大图上的节点表征学习中,使用PyG库用于数据加载的DataLoader,ClusterLoader等类时,会涉及到参数num_workers。在不同系统环境试验该参数的设定值时,会出现不同的结果 colab(Linux),设为12 有warning message,提示num_workers参数设置不合理,不影响继续运行 /usr/local/lib/python3.7/di 阅读详情

相关推荐

神经网络学习笔记-波尔兹曼(Boltzmann)机

Boltzmann机是一种反馈随机神经网络 两种输出状态,单极性0或1。输出状态的取值根据概率统计法则决定,这种概率统计法则类似Boltzmann分布,部分反馈 它在神经元状态变化中引入了统计概率,网络的平衡状态服从Boltzmann分布,网络运行机制基于模拟退火算法。 离散Hopfield神经网络+模拟退火+隐单元=Boltzman机 DHNN全反馈,转移函数:符号函数 BM机网络拓扑...

求易学堂的博客 2617

通配符上下限记录

通配符上下限记录

pbuzhidaol的博客 2179

STK中的传感器与通信组件:雷达、发射机、接收机与天线的协同建模

本文深入解析了STK软件中传感器、雷达、发射机、接收机与天线的协同建模方法。通过构建一个完整的卫星对地数传系统实例,阐述了各组件如何分工协作,实现从几何指向到射频链路的高保真仿真,为复杂航天通信与探测系统的设计与分析提供了高效、灵活的解决方案。

weixin_29271263的博客 273

Java 泛型

无论在何时,只要你能做到,你就应该尽量使用泛型方法。

KEEP CODING 4865

告别WSL!用AMD ROCm7在Windows上跑ComfyUI,我的幻x2025 AI Max+ 395体验与性能调优心得

本文详细介绍了在搭载AMD Ryzen AI Max+ 395处理器的幻x2025笔记本上,如何通过ROCm7在Windows原生环境下高效运行ComfyUI,并分享了一系列性能调优技巧和实战经验,帮助开发者摆脱WSL束缚,提升AI创作效率。

weixin_29102619的博客 1117

从爱好者到工程师:如何低成本体验RFSoC ZU47DR?性能实测与避坑指南

本文详细介绍了如何低成本体验赛灵思RFSoC ZU47DR芯片,包括二手开发板选购、租赁服务、精简开发环境搭建等实用方案。通过性能实测与避坑指南,帮助爱好者和工程师在有限预算下充分利用这款集成了ARM处理器和FPGA的高性能芯片,降低开发门槛。

weixin_42525005的博客 221

集合-ArrayList

ArrayList是Java中的一个类,实现了List接口,可以动态的增加或者减少元素的大小ArrayList是一个数组实现的列表,它可以存储不同类型的元素,如整数、字符串、对象等。ArrayList的容量会根据需要自动增加或减小,因此不需要手动指定大小。ArrayList还提供了许多有用的方法,如add()、remove()、get()等,可以方便地操作列表中的元素。

陆卿之的博客 921

潜流式人工湿地计算_复合人工湿地净化水产养殖污水技术

技术概论1. 定义与分类人工湿地是指用人工筑成水池或沟槽,底面铺设防渗漏隔水层,充填一定深度的基质层,种植水生植物,利用基质、植物、微生物的物理、化学、生物三重协同作用使污水得到净化的系统。当池塘养殖污水进入人工湿地时,其污染物被床体吸附、过滤、分解而达到水质净化作用。按照水体流动方式,人工湿地分为表面流人工湿地、水平潜流人工湿地、垂直潜流人工湿地以及由前面几种湿地混合搭配的组合式人工湿...

weixin_42365490的博客 4249

C#基础之泛型Generic

泛型Generic 允许一个可以与任何数据类型一起工作的类或方法 优点: 1.性能 不需要拆箱和装箱 2.类型安全 和object类相比,可以根据需要用特定的类型替换泛型类型,保证了数据安全类。 3.二进制代码重用 一次定义多次使用 4.代码的扩展 对于引用类型,会共享本地类的所有相同代码。只有值类型,才会每次实例化一个新类 特性: 使用泛型是一种增强程序功能的技术,具体如下: 1.利于最大程度地重用代码、保护类型的安全以及提高性能。 2.可以创建泛型集合类。.NET框架类库.

shanshan2099的专栏 367

Klipper-源码模块

Klippy上位机程序采用多线程架构,包含四个主要进程:主线程处理G代码命令、串口IO线程(C语言实现)、MCU响应处理线程(Python实现)和独立日志线程。代码结构分为三大模块:chelper(C语言辅助函数)、extras(扩展功能如温度控制)和kinematics(运动学模型实现)。核心功能模块包括串口通信(serialhdl.py)、步进电机控制(stepper.py)、运动学计算等,其中C语言实现的底层模块通过serialqueue.c处理高效IO。扩展模块支持热床、传感器等外设,运动学子模块提

weixin_44258581的博客 992

YOLOv11改进 | 注意力篇 | YOLOv11引入CBAM注意力机制

我们的实验表明各种模型的分类和检测性能得到了一致的改进,证明了 CBAM 的广泛适用性。简单介绍:CBAM的主要思想是通过关注重要的特征并抑制不必要的特征来增强网络的表示能力。模块首先应用通道注意力,关注"重要的"特征,然后应用空间注意力,关注这些特征的"重要位置"。到此本文的正式分享内容就结束了,在这里给大家推荐我的YOLOv11改进有效涨点专栏,本专栏目前为新开的,后期我会根据各种前沿顶会进行论文复现,也会对一些老的改进机制进行补充,如果大家觉得本文帮助到你了,订阅本专栏,关注后续更多的更新~

tsg6698的博客 2万+

Kotlin泛型实战精要(资深架构师20年经验倾囊相授)

掌握Kotlin泛型编程核心技巧,解决类型安全与代码复用难题。涵盖协变逆变、泛型函数、约束边界等关键概念,适用于Android开发与通用库设计。提升代码健壮性与可维护性,资深架构师实战经验总结,值得收藏。

ByteChat的博客 797

Google Earth Engine 红树林制图方法的代码(三个模块)

谷歌地球引擎红树林制图方法(GEM)提供了一种直观、易用且可复制的工具,适用于广泛的非专业沿海管理者和决策者。该工具全面回顾并整合了相关的红树林遥感文献,并利用云计算的力量,包括简化的基于图像的潮汐校准方法。GEM 可免费用于非营利用途,并在 GEE 中运行,包含全面且详细注释的代码。GEM 专门设计用于绘制全球范围内多日期红树林分布并量化其动态变化。目前可用的工具和方法未能充分利用沿海管理者通常持有的丰富地方专业知识——GEM 致力于填补这一空白,帮助结合地方专业知识与 GEE 的云计算能力。

此星光明博客 560

Kotlin学习手记——反射,安卓资深架构师分享学习经验及总结

} } class A(val name : String, sex: Boolean) : S(sex) { fun String.hello(){ } fun foo() { } var age : Int = 0; } fun A.test() { } 上面的class A继承了一个类,并且类的内部有其他类定义的扩展方法,本身也定义了一个扩展方法。 KClass提供了很多方法获取类的属性和方法,但是有一些区别,方法比较多,可以看一下区别: fun main() { // 能得到:age, name,

m0_66264673的博客 258

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

Kotlin 的泛型简单理解,sharedpreferences原理

本人从事Android开发已经有十余年,算是一名资深的移动开发架构师了吧。根据我的观察发现,对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。所以在此将我十年载,从萌新小白一步步成长为Android移动开发架构师的学习笔记,从Android四大组件到手写实现一个架构设计,我都有一一的对应笔记为你讲解。当然我也为你们整理好了百度、阿里、腾讯、字节跳动等等互联网超级大厂的历年面试真题集锦。

2401_84003554的博客 648

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

安卓资深架构师分享学习经验及总结,吊打面试官系列!_资深安卓系统架构师面试

给大家送上我成功跳槽复习中所整理的资料,由于文章篇幅有限,所以只是把题目列出来了网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。需要这份系统化学习资料的朋友,可以戳这里获取一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2401_84520377的博客 692

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Kotlin接口设计中的陷阱与解决方案(资深架构师20年经验总结)

解决Kotlin接口设计常见陷阱,提升代码可维护性。结合实际开发场景,详解默认方法冲突、多继承问题及扩展函数优化策略,助力构建高内聚低耦合系统。资深架构师经验提炼,值得收藏。

CompiWander的博客 766

2025 Kotlin/Native开发者薪资报告:从入门到架构师的薪资跃迁之路

你还在为移动端跨平台开发技术选型发愁吗?想知道掌握Kotlin/Native能为你的薪资带来多大提升吗?本文将深入分析2025年Kotlin/Native开发者的市场薪资水平、技能需求及职业发展路径,助你规划技术成长蓝图。 ## 一、Kotlin/Native市场需求与薪资现状 Kotlin/Native作为JetBrains推出的原生开发技术,已广泛应用于移动端、嵌入式和跨平台开发领域。根据

gitblog_00127的博客 1185

Kotlin开发者眼中的Java缺少哪些特性?

出品 | OSC开源社区(ID:oschina2013)Nicolas Fränkel 是一名资深程序员,拥有近二十年的 Java 开发经历。他在几年前开始学习 Kotlin,在此之后,每当他再使用 Java 时,总会思考为什么自己写的 Java 代码看起来不如 Kotlin 那么优雅,并且发现 Java 缺少一些可以提升代码可读性、表现力和可维护性的功能。对此,Nico...

程序猿DD 419

Android面试Kotlin高阶之必问三连,真香系列

面试题集可以帮助你查漏补缺,有方向有针对性的学习,为之后进大厂做准备。但是如果你仅仅是看一遍,而不去学习和深究。那么这份面试题对你的帮助会很有限。最终还是要靠资深技术水平说话。网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。建议先制定学习计划,根据学习计划把知识点关联起来,形成一个系统化的知识体系。学习方向很容易规划,但是如果只通过碎片化的学习,对自己的提升是很慢的。

m0_62289824的博客 1022

Kotlin委托模式应用全揭秘(资深架构师20年经验总结)

掌握Kotlin委托模式高效解决类扩展与代码复用难题,详解属性委托、接口委托在实际开发中的应用技巧与优势。涵盖常见使用场景与最佳实践,提升代码可读性与维护性,值得收藏。

FuncFun的博客 960

【资深架构师亲授】:Kotlin在高并发场景下的性能调优实践

掌握Kotlin性能优化实践,解决高并发场景下的响应延迟与资源消耗难题。结合协程、内存管理与代码优化技巧,提升系统吞吐量与稳定性,适用于大规模并发服务架构。方法实用、效果显著,值得收藏。

FastSolve的博客 315

Kotlin开发App必须掌握的8个高级技巧(资深架构师亲授)

掌握Kotlin开发App的8个高级技巧,提升开发效率与代码质量。涵盖协程异步处理、DSL构建UI、依赖注入等核心方法,适用于高性能Android应用开发。架构清晰、扩展性强,值得收藏。

CompiGlow的博客 727

数据绑定性能优化全解析,资深架构师亲授Kotlin高效秘诀

掌握Kotlin数据绑定实践中的性能优化技巧,提升Android开发效率。涵盖视图绑定、生命周期管理与内存优化等核心方法,适用于复杂列表与高频更新场景。减少卡顿,提高响应速度,资深架构师经验总结,值得收藏。

DebugLoom的博客 776

【图文全解】资深Android架构师的成长之路,从入门到精通!

前言 众所周知,当下流行的编程语言有Java、PHP、C、C++、Python、Go等。其中,稳坐榜首的仍然是Java编程语言,且在以面向对象思想占主导的应用开发中,Java往往成为其代名词。 Java语言的背景强大,开发者众多,一直发展都不错。从普遍的企业的角度来看,存在的问题是:后台被认为是技术核心,客户端却被认为技术含量不高,甚至小企业会让后台人员顺便开发简单的客户端,或者让后台的架构师管理...

迪士尼在逃程序员の博客 303
上一篇: 资深架构师:深入聊聊获取屏幕高度这件事
下一篇: 互联网公司忽悠员工的黑话,套路太深了
懂你的大海
博客等级 码龄6年 9049粉丝 1381原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值