当前位置: 首页 > news >正文

Kotlin学习(一)

1. Kotlin 作用域函数

如果同学们已经在项目中用过 Kotlin 语言,那么一定见过 let 函数!因为每当 Kotlin 检测到某个对象可能为空时,会自动帮我们修改为用 let 函数实现:user.name?.let{ textView.text = it }。这里的 let 函数就是 Kotlin 的作用域函数。除了 let,还有 run、with、apply、also 等等作用域函数。

作用域函数是 Kotlin 内置的,可对数据做一系列操作、变换的函数,与集合操作符类似,作用域函数不仅仅可被集合对象调用,它们还可以被所有对象调用。让我们来看看它们的用法。

let 和 run 函数类似,都会返回函数内闭包的结果,区别在于 let 有闭包参数,而 run 没有闭包参数。使用方法:let{ 闭包 }、run{ 闭包 },有闭包参数意思是 let 在闭包中可以通过 it 拿到它自己本身;而 run 就不行了,只能通过 this 关键字拿到它本身。看 code 1 例子。

// code 1
data class Car(val brand: String,val price: Int
)var car: Car? = null
fun funcExample() {car = Car("红旗", 199999)// let 闭包里可用 it 访问调用者,可返回闭包的执行结果val carBrand = car?.let { "car's brand is ${it.brand}" }println(carBrand)// run 闭包用 this 访问调用者val carPrice = car?.run { "car's price is ${this.price}" }println(carPrice)
}

also和 apply 函数不会返回闭包里的结果,而上述的 let 和 run 是可以返回闭包结果的。also和 apply 函数的区别也是在于有无闭包参数:also 有闭包参数,apply 没有闭包参数。但是它们都会返回调用者对象,所以它们支持链式调用。

// code 2
// also 闭包里可用 it 访问调用者,后面可链式调用
car?.also {println("car 贴牌前 brand = ${it.brand}")
}?.brand = "比亚迪"
// apply 闭包用 this 访问调用者,后面也可链式调用
car?.apply {println("car 贴牌后 brand = ${this.brand}")
}?.apply {println("car's price = ${this.price}")
}

takeIf 和 takeUnless 这两个作用域函数就用的相对较少了。takeIf 函数里的闭包返回的是 Boolean 类型,如果闭包条件满足,则返回调用者本身,如果不满足,则返回 null。举个栗子来说明吧。

// code 3
car?.takeIf { it.price > 1500000 }?.also { println("车太贵啦!") }    // 闭包为 true,则不为空,执行 also 函数闭包?: run { println("价格还行!") }    // 闭包为 false,则返回空,执行 run 函数闭包

takeUnless 跟 takeIf 是相反的关系,takeUnless 的闭包条件满足则返回空,不满足则返回调用者自己。

repeat 函数。调用方法:repeat( times ) { 闭包 }。将闭包的操作执行 times 次。闭包里面的 it 是当前执行的循环次数,从 0 开始计数。

// code 4
repeat(3) {println("car' brand is ${car?.brand}, price is ${car?.price} 当前执行次数为:$it")
}//执行结果:
//car' brand is 比亚迪, price is 199999 当前执行索引为:0
//car' brand is 比亚迪, price is 199999 当前执行索引为:1
//car' brand is 比亚迪, price is 199999 当前执行索引为:2

with 函数。调用方法:with( T ){ 闭包 }。就是将对象 T 去执行闭包里的操作,通常在 Android 开发中,需要对一个 TextView 赋值时,就可以使用 with,比较方便:

// code 5
with(textView) {text = "测试"textSize = 20FsetTextColor(ContextCompat.getColor(context, R.color.purple_200))
}

2. Kotlin 自定义操作符

学习 Kotlin 一段时间后,你会发现 Kotlin 给了开发者很大的自我发展空间。比如:支持对类新增扩展函数,支持运算符重载等。所以,我们自己也可以自定义一些操作符,来方便开发。看过 Kotlin 自带的操作符实现的同学们会发现,这些函数都是 inline 关键字修饰的。我们先看下 inline 关键字。

inline 关键字,可以看做是一个是否 内联 的标记。被修饰的函数会在编译时,直接把函数体一起“拷贝”过去,就是将内联函数的代码直接放在内联函数的位置上,这与一般函数不同,在调用一般函数的时候,是指令跳转到被调用函数的入口地址,执行完被调用函数后,指令再跳转回原来跳进来的地方继续执行后面的代码;而由于内联函数是将函数的代码直接放在了函数的位置上,所以没有指令跳转,指令按顺序执行。这样做可以加快代码的运行速度,但是会增加编译时间以及编译后的代码量。

inline 关键字适合修饰不太复杂的但会频繁调用的函数。所以 Kotlin 自带的操作符都是 inline 函数,我们如果要自定义一个操作符,也是需要修饰为 inline 函数。如下就是自定义了一个 convert 操作符,功能类似集合中的 map 函数。

// code 6
inline fun <T, E> Iterable<T>.convert(action: (T) -> E): Iterable<E> {val list: MutableList<E> = mutableListOf()for (item in this) list.add(action(item))return list
}

3. Kotlin 中反引号 ` 的用法

在前面的《Kotlin 学习笔记(一)》 中介绍了下 Kotlin 反引号处理 Kotlin 关键字在 Java 代码里冲突的问题。反引号还有一个作用,就是在 Kotlin 代码中将一个不合法的字符转变为合法字符。举个栗子:

// code 7
object SmallTips {// 反引号可将非法字符转换为合法字符fun `123`(){println("函数名居然为`123`!")}fun ` `(){println("函数名居然为` `!")}fun `  `(){println("函数名居然为`  `!")}
}// 调用也需将反引号加上
SmallTips.`123`()
SmallTips.` `()
SmallTips.`  `()

不可思议吧!函数名本来不能为纯数字或空格符,但是加上反引号就可以了!神奇!那么这有啥用?还记得 Kotlin 的 internal 访问修饰符吗?它限定了被它修饰的函数只能在当前模块使用,而不能在其他模块使用。但是 Java 中是没有这个修饰符的,而 Kotlin 和 Java 又必须完全兼容,所以 Java 也不得不支持这一特性。

那么问题来了,通过反编译查看 Kotlin 中 internal 修饰的函数,在生成的 Java 代码里被编译成了 public 修饰的函数(笑Cry.gif)。为了让 Java 不能访问 Kotlin 中的函数,可以在 Kotlin 中将这些函数的命名改为不合法的形式,然后用反引号包起来,这么做之后,Java 代码是不能调用这些方法的,而 Kotlin 可以调用,从而可以实现在 Java 中屏蔽某些 Kotlin 函数的效果。最后,这种反引号的用法不推荐使用!了解即可!

4. Kotlin 对象比较

在 Java 中,要比较两个对象是否相等,通常用的是 == 或 equals 方法。Java 中的 == 运算符比较的是两个对象本身的值,即两个对象在内存中的首地址。如果是两个字符串,就是比较的这两个字符串存储的地址是否是同一个。

Java 中,对象的首地址是它在内存中存放的起始地址,它后面的地址是用来存放它所包含的各个属性的地址,所以内存中会用多个内存块来存放对象的各个属性值,而通过这个首地址就可以找到该对象,进而可以找到该对象的各个属性。

Java 中的 equals 方法比较的是两个对象中各个属性值的是否相同。如果是两个字符串,就是比较的两字符串所包含的内容是否相同。

在 Kotlin 语言中,判断两个对象是否相等用的是 == 和 ===。没错,两个等号和三个等号。

Kotlin 的 == 相当于 Java 中的 equals 方法;

Kotlin 的=== 相当于 Java 中的 == 运算符,记住即可。栗子也有,看下方:

// code 8
val str1 = java.lang.String("我发")
val str2 = java.lang.String("我发")
println("str1 == str2 结果为 ${str1 == str2}") // 输出:str1 == str2 结果为 true
println("str1 === str2 结果为 ${str1 === str2}") // 输出:str1 === str2 结果为 false

因为在 Kotlin 的 String 构造方法中,不能直接传入一个字符串,所以这里用的是 Java 中的 String 类进行的初始化。也可以用 Kotlin 的 String 另外两种初始化方法:

1)val str1 = StringBuilder("我发").toString();

2)val str1 = String("我发".toByteArray())。

5. Kotlin 的常量变量

根据笔记一中的内容,我们知道,Kotlin 有两种变量,一种是用 val 关键字修饰的不可变的变量;另一种是用 var 关键字修饰的可变的变量。如何在类中对这两种变量进行初始化呢?val 因为是不可变,所以只能重写变量的 getter 方法,var 则可以重写 getter 和 setter 方法,当然类会自动帮我们生成 getter 和 setter 方法。

// code 9
class Person {    // 此类无实际意义,为了举个栗子而已var age: Int = 0 // 可变变量我们可以重写 getter 和 setterget() {return field.plus(10)}set(value) {field = value - 1}val name: String = "" // 不可变变量我们只能重写 getter 方法get() {return field + "haha"}val height: Float // height 是用 val 修饰的,但 height 并不是一个常量get() {return (age * 2F + 10)}
}

在重写 getter 和 setter 方法时,可以通过 field 拿到该属性的值。val 和 var 最本质的区别就是,val 没有 setter 方法。val 并不是常量,而是说,我们不能再次对 val 变量进行赋值操作!为啥 val 修饰的并不是常量?可以看一下 code 9 中的 height 变量,当 age 变化时,height 也会变化,它并不是一个常量。

如果要声明一个常量,则要用到 const 关键字。它有两个注意点:

1)const 只能修饰 object 的属性,或 top-level 变量。

2)const 变量的值必须在编译期间就确定下来,所以类型只能是 String 或基本数据类型。

啥意思呢?我理解的就是,Kotlin 中用 const 修饰的常量类似于 Java 中的一个不可变的静态变量。它声明的地方只有三种1. object 类的内部,object 修饰的都是静态类;2. top-level 位置,也就是在一个类的外部进行声明;3. companion object 内部,也就是用于声明静态变量的位置。

// code 10
object ValAndVarExample {const val t2 = "heiheihei"
}const val t1 = "hahaha" // top-level,类外class Person {companion object{const val t3 = "hehehe"}
}

6. Kotlin 的 inline、crossinline、noinline 关键字的特殊使用

在前面的第2节 Kotlin 的自定义操作符中,已经说明了 inline 关键字的基本用法,知道了内联函数可以通过直接将代码拷贝到调用的地方从而加快程序执行速度的特性。除了 inline 关键字外,还有 crossinline 和 noinline 两个关键字,来看看它们还有什么特殊的用法。

在讲之前,还是需要明白一些前提知识。inline 关键字既会影响函数对象本身,也会影响传入的 Lambda 闭包参数,两者都会被内联到调用点。

编译预处理器会对内联函数进行扩展,省去了参数压栈、生成汇编语言的 CALL 调用、返回参数、执行 return 等过程,从而提高运行速度。优点是,在函数被内联后编译器可以通过上下文相关的优化技术对结果代码执行更深入的优化;但会使得编译后的代码体积变大,只是省去了函数调用的开销。所以 inline 适合用于较简单的频繁调用的函数。

6.1. 被 inline 修饰的函数中的 Lambda 表达式,可以中断外部函数的调用。
// code 11
fun main(args: Array<String>) {test1 {println("我要好好学 Kotlin")return}println("我要好好学习 Android")
}inline fun test1(lambda: () -> Unit) {lambda.invoke()
}
// 输出:我要好好学 Kotlin

test1 函数被 inline 修饰,它有个 Lambda 闭包,在该闭包中有个 return 返回函数,这个函数可以中断外部的 main 函数,所以只会输出 “我要好好学 Kotlin”。

通常情况下,Kotlin 中函数内部 Lambda 闭包是不能中断外部函数的执行的,可以尝试下将 code 11 中 test1 修饰的 inline 去掉,此时编译器就会提示 return 只能写成 return@test1,即只能返回 test1 函数,并不能返回 main 函数。

6.2. crossinline 关键字不允许被 inline 修饰的函数中的 Lambda 表达式中断外部函数的执行。

意思就是,在 code 11 中,如果 Lambda 表达式的 return 只是想中断该闭包的执行,而不想中断外部 main 函数的执行,该咋办?有人会说,那我不用 inline 不就可以了?但这里又需要用 inline 呢?那就可以使用 crossinline 去修饰这个 Lambda 闭包,编译器就不会去对这个 Lambda 表达式做内联操作。

// code 12
fun main(args: Array<String>) {test1 {println("我要好好学 Kotlin")return@test1println("我不想学习了~")}println("我要好好学习 Android")
} inline fun test1(crossinline lambda: () -> Unit) {lambda.invoke()
}
// 输出:
//我要好好学 Kotlin
//我要好好学习 Android
6.3. noinline 关键字不允许被 inline 修饰的函数中的 Lambda 表达式被内联处理。

首先,noinline 关键字是作用于 Lambda 闭包的;其次,它是用于在修饰了 inline 关键字的函数中,剔除 inline 关键字对 Lambda 闭包的影响,让它就作为一个普通的 Lambda 闭包。说明不够,代码来凑!

// code 13
inline fun test2(lambda0: () -> Unit, noinline lambda1: () -> Unit): () -> Unit {lambda0.invoke()lambda1.invoke()return lambda1
}

test2 函数被 inline 修饰,有两个 Lambda 闭包作为参数,而且它的返回值也是一个 Lambda 闭包。如果 lambda1 没有 noinline 关键字修饰,那么它就会跟 lambda0 一样,将函数体直接拷贝到调用的地方,这种情况下,lambda1 就不能作为闭包返回了,所以去掉 noinline 之后,code 13 代码会报错。所以,这里如果要将 test2 用 inline 修饰,同时,又想返回一个闭包的话,就可以用 noinline 关键字去除 inline 对闭包的影响。

上面说的都是关于 inline 关键字的进阶用法,通常情况下不会用到,作为知识储备即可。

相关文章:

Kotlin学习(一)

1. Kotlin 作用域函数 如果同学们已经在项目中用过 Kotlin 语言&#xff0c;那么一定见过 let 函数&#xff01;因为每当 Kotlin 检测到某个对象可能为空时&#xff0c;会自动帮我们修改为用 let 函数实现&#xff1a;user.name?.let{ textView.text it }。这里的 let 函数就…...

鸿蒙UI开发——日历选择器

1、概 述 在项目开发中&#xff0c;我们时常会用到日历选择器&#xff0c;效果如下&#xff1a; ArkUI已经为我们提供了组件&#xff0c;我们可以直接使用&#xff0c;下面针对日历组件做简单介绍。 2、CalendarPickerDialog 接口定义如下&#xff1a; // 定义日历选择器弹…...

2025-1-9 QT 使用 QXlsx库 读取 .xlsx 文件 —— 导入 QXlsx库以及读取 .xlsx 的源码 实践出真知,你我共勉

文章目录 1. 导入QXlsx库2. 使用 QXlsx库 读取 .xlsx 文件小结 网上有很多教程&#xff0c;但太费劲了&#xff0c;这里有个非常简便的好方法&#xff0c;分享给大家。 1. 导入QXlsx库 转载链接 &#xff1a;https://github.com/QtExcel/QXlsx/blob/master/HowToSetProject.md…...

React中createRoot函数原理解读——Element对象与Fiber对象、FiberRootNode与HostRootNode

【2024最新版】React18 核心源码分析教程&#xff08;全61集&#xff09; Element对象与Fiber对象 在 React 中&#xff0c;Element 对象 和 Fiber 对象 是核心概念&#xff0c;用于实现 React 的高效渲染和更新机制。以下是它们的详细解读&#xff1a; 1. Element 对象 定…...

利用Python实现Union-Find算法

Union-Find&#xff08;又称 并查集&#xff09;是一种高效解决 动态连通性问题 的算法。它主要提供两种操作&#xff1a; Union(x, y)&#xff1a;将元素 x 和 y 连接。Find(x)&#xff1a;找到元素 x 所属的集合的标识符&#xff08;通常是集合的根节点&#xff09;。 常用…...

【LeetCode: 912. 排序数组 + 归并排序】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…...

AI时代来了,我们不再需要IDE了

大家好&#xff0c;我是编程乐趣。 最近在思考一个问题&#xff0c;那就是AI这么强大。 未来有没有可能&#xff0c;我们就不需要不需要开发工具了&#xff0c;只需一个浏览器就可以开发软件了。 一、AI带来的变化 1、代码生成与补全 AI工具如GitHub Copilot等能够根据代码…...

PL/SQL语言的网络编程

PL/SQL语言的网络编程 引言 在信息化迅速发展的今天&#xff0c;网络编程作为现代软件开发的重要组成部分&#xff0c;受到了广泛关注。而在数据库管理系统中&#xff0c;Oracle 提供了 PL/SQL&#xff08;Procedural Language/Structured Query Language&#xff09;&#x…...

vue video重复视频 设置 srcObject 视频流不占用资源 减少资源浪费

// 直接设置srcObject减少获取视频流&#xff1a;通过 captureStream() 方法从下方视频元素获取视频流。 // 设置 srcObject&#xff1a;将获取到的视频流设置为上方视频的 srcObject 减少资源浪费 // 获取到需要复制到的dom元素 const firstVideoElement proxy.$refs.firs…...

JavaFx 21 项目Markdown 预览、编辑、新建、文件树、删除、重命名

项目文件结构 项目的源代码和资源文件存放在以下路径: 源代码: src/main/java/com/kong/markdown/ 包含多个 Java 文件,主要实现了应用的功能: App.java:主类,可能包含应用的启动逻辑。FileService.java:可能与文件操作相关的服务类。MainController.java:控制器类,可…...

git项目提交步骤(简洁版)

1.创建仓库 2.填写 信息 3.点击这个按钮 4.找到要上传的文件&#xff0c;在目录内右键点击 5.依次执行命令 在命令窗口中输入&#xff1a;git init 复制仓库地址&#xff1a; 在命令窗口中输入&#xff1a;git remote add origin 仓库地址 在命令窗口中输入&#xff1a;…...

风水算命系统架构与功能分析

系统架构 服务端&#xff1a;Java&#xff08;最低JDK1.8&#xff0c;支持JDK11以及JDK17&#xff09;数据库&#xff1a;MySQL数据库&#xff08;标配5.7版本&#xff0c;支持MySQL8&#xff09;ORM框架&#xff1a;Mybatis&#xff08;集成通用tk-mapper&#xff0c;支持myb…...

Clojure语言的学习路线

Clojure语言的学习路线 Clojure是一种现代的Lisp方言&#xff0c;运行于Java虚拟机&#xff08;JVM&#xff09;上。它具备强大的函数式编程特性&#xff0c;支持并发和多线程编程&#xff0c;适合处理复杂的数据和计算任务。由于其简洁和灵活的语法&#xff0c;Clojure在数据…...

网络安全核心目标CIA

网络安全的核心目标是为关键资产提供机密性(Confidentiality)、可用性(Availablity)、完整性(Integrity)。作为安全基础架构中的主要的安全目标和宗旨&#xff0c;机密性、可用性、完整性频频出现&#xff0c;被简称为CIA&#xff0c;也被成为你AIC&#xff0c;只是顺序不同而已…...

Wi-Fi Direct (P2P)原理及功能介绍

目录 Wi-Fi Direct &#xff08;P2P&#xff09;介绍Wi-Fi Direct P2P 概述P2P-GO&#xff08;P2P Group Owner&#xff09;工作流程 wifi-Direct使用windows11 wifi-directOpenwrtwifi的concurrent mode Linux环境下的配置工具必联wifi芯片P2P支持REF Wi-Fi Direct &#xff…...

Perl语言的数据结构

Perl语言的数据结构 Perl是一种功能强大的、灵活的脚本语言&#xff0c;广泛用于文本处理、系统管理、网络编程以及许多其他领域。其灵活性不仅体现在语法上&#xff0c;还体现在其丰富的数据结构上。本文将深入探讨Perl的主要数据结构&#xff0c;包括标量、数组、哈希以及引…...

【MFC】设置CTreeCtrl单个节点的文字颜色

问题 功能调整需要依据不同状态设置树控件中单个节点的文字颜色。 分析 1、CTreeCtrl本身有设置文字颜色的接口SetTextColor&#xff0c;但是这个接口是设置树控件整体的文字颜色。 2、在自定义接口可以对树控件单个节点进行更新文字颜色和背景颜色&#xff0c;接收自定义绘制…...

【CSS】设置滚动条样式

文章目录 基本语法用法案例 基本语法 在CSS中&#xff0c;可以使用 ::-webkit-scrollbar 和相关伪元素来为滚动条设置样式&#xff0c;但请注意这些伪元素是非标准的&#xff0c;主要用于WebKit内核浏览器&#xff08;如Chrome、Safari&#xff09;。 ::-webkit-scrollbar CSS …...

Gitlab-Runner配置

原理 Gitlab-Runner是一个非常强大的CI/CD工具。它可以帮助我们自动化执行各种任务&#xff0c;如构建、测试和部署等。Gitlab-Runner和Gitlab通过API通信&#xff0c;接收作业并提交到执行队列&#xff0c;Gitlab-Runner从队列中获取作业&#xff0c;并允许在不同环境下进行作…...

代码随想录 哈希 test 8

18. 四数之和 - 力扣&#xff08;LeetCode&#xff09; 与三数之和类似&#xff0c;重点在剪枝和去重的区别&#xff0c;由于target可正可负&#xff0c;因此需要分两种情况讨论&#xff0c;如果target为正&#xff0c;则若当前选择的元素之和大于target&#xff0c;需要跳出这…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

UDP(Echoserver)

网络命令 Ping 命令 检测网络是否连通 使用方法: ping -c 次数 网址ping -c 3 www.baidu.comnetstat 命令 netstat 是一个用来查看网络状态的重要工具. 语法&#xff1a;netstat [选项] 功能&#xff1a;查看网络状态 常用选项&#xff1a; n 拒绝显示别名&#…...

Linux简单的操作

ls ls 查看当前目录 ll 查看详细内容 ls -a 查看所有的内容 ls --help 查看方法文档 pwd pwd 查看当前路径 cd cd 转路径 cd .. 转上一级路径 cd 名 转换路径 …...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互

引擎版本&#xff1a; 3.8.1 语言&#xff1a; JavaScript/TypeScript、C、Java 环境&#xff1a;Window 参考&#xff1a;Java原生反射机制 您好&#xff0c;我是鹤九日&#xff01; 回顾 在上篇文章中&#xff1a;CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计

随着大语言模型&#xff08;LLM&#xff09;参数规模的增长&#xff0c;推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长&#xff0c;而KV缓存的内存消耗可能高达数十GB&#xff08;例如Llama2-7B处理100K token时需50GB内存&a…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

LOOI机器人的技术实现解析:从手势识别到边缘检测

LOOI机器人作为一款创新的AI硬件产品&#xff0c;通过将智能手机转变为具有情感交互能力的桌面机器人&#xff0c;展示了前沿AI技术与传统硬件设计的完美结合。作为AI与玩具领域的专家&#xff0c;我将全面解析LOOI的技术实现架构&#xff0c;特别是其手势识别、物体识别和环境…...

协议转换利器,profinet转ethercat网关的两大派系,各有千秋

随着工业以太网的发展&#xff0c;其高效、便捷、协议开放、易于冗余等诸多优点&#xff0c;被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口&#xff0c;具有实时性、开放性&#xff0c;使用TCP/IP和IT标准&#xff0c;符合基于工业以太网的…...