Kotlin 高级语法深度解析

  • 1. 协程(Coroutines)
    • 1.1 基础概念
      • 1.挂起和恢复
      • 2.协程构建器 (Coroutine Builders)
      • 3.协程作用域
      • 4.调度器
    • 1.2 核心用法
    • 1.3 实战示例
  • 2. 密封类(Sealed Classes)
    • 2.1 定义与特性
    • 2.2 模式匹配
    • 2.3 应用场景
  • 3. 内联函数(Inline Functions)
    • 3.1 基础用法
    • 3.2 关键字扩展
  • 4. 扩展函数(Extension Functions)
    • 4.1 基础定义
    • 4.2 最佳实践
  • 5. 类型系统进阶
    • 5.1 空安全
    • 5.2 泛型系统
  • 6. 作用域函数(Scope Functions)
  • 7. 数据类与解构
    • 7.1 数据类特性
    • 7.2 解构扩展
  • 8. 委托 (Delegation)
    • 8.1 类委托
    • 8.2 属性委托
  • 9.高阶函数 和 Lambda 表达式
  • 10. 高级特性应用
    • 10.1 DSL构建
    • 10.2 反射与元编程
  • 11.Flow 数据流处理
    • 11.1创建 Flow
    • 11.2 Flow 的生命周期操作符
    • 11.3流上下文 (Context) 与 flowOn

1. 协程(Coroutines)

协程是 Kotlin 处理异步编程和并发的利器,它允许你以同步的代码风格编写异步逻辑,避免了回调地狱

1.1 基础概念

1.挂起和恢复

暂停当前协程的执行,并释放它占用的线程资源,让线程去执行其他任务。当挂起的操作(如网络请求返回)完成后,协程会在合适的线程上恢复执行。

// 声明一个挂起函数
suspend fun fetchUserData(): User {// ... 执行耗时操作,如网络请求return withContext(Dispatchers.IO) { // 切换到 IO 线程池执行// 模拟网络请求delay(1000) // 这是一个挂起函数,非阻塞地延迟User("John") // 返回结果}
}
// 挂起函数只能在另一个挂起函数或协程中被调用

2.协程构建器 (Coroutine Builders)

用于启动一个新的协程。

  • launch: 启动一个新协程,不返回结果。用于执行一段“一劳永逸”的工作(Fire-and-forget)。

fun main() = runBlocking {val job = launch { // 返回一个 Job 对象,用于管理协程delay(1000L)println("World!")}println("Hello,")job.join() // 等待协程执行完毕
}
// 输出: Hello, (等待1秒) World!
  • async: 启动一个新协程,并返回一个 Deferred 对象(一个轻量级的、带有结果的
    Future)。用于并行执行任务并获取结果,通常与 await() 一起使用。
suspend fun concurrentSum(): Int = coroutineScope {val deferred1 = async { fetchData1() } // 立即启动异步任务1val deferred2 = async { fetchData2() } // 立即启动异步任务2deferred1.await() + deferred2.await() // 等待两个任务都完成并求和
}

3.协程作用域

通过coroutineScope、viewModelScope等管理生命周期

GlobalScope: 全局作用域,生命周期与应用程序一样长。应谨慎使用,容易造成协程泄漏。

coroutineScope: 一个挂起函数,用于创建一个新的作用域,它会等待所有子协程完成后才完成自身。如果子协程失败,它会取消所有其他子协程并传播异常。

supervisorScope: 类似 coroutineScope,但子协程的失败不会导致其他子协程取消( supervision )。适用于独立的并行任务。

Android 中的生命周期感知作用域:

viewModelScope (在 ViewModel 中使用)

lifecycleScope (在 Activity/Fragment 中使用)

4.调度器

决定协程在哪个或哪些线程上执行

Dispatchers.Main: 在主线程(UI线程)上执行。用于更新 UI 和进行轻量级操作。

Dispatchers.IO: 专为磁盘和网络 I/O 操作优化。使用共享的线程池。

Dispatchers.Default: 专为 CPU 密集型计算任务优化。使用共享的线程池,其大小与 CPU 核心数相同。

Dispatchers.Unconfined: 不限制任何特定线程。不推荐新手使用。

1.2 核心用法

// 结构化并发示例
viewModelScope.launch {try {val user = async { fetchUser() }val news = async { fetchNews() }updateUI(user.await(), news.await())} catch (e: Exception) {showError(e)}
}// 线程切换
suspend fun loadData() = withContext(Dispatchers.IO) {// 网络请求
}

1.3 实战示例

Android 中的典型用法

// 在 ViewModel 中
class MyViewModel : ViewModel() {// 使用 viewModelScope,当 ViewModel 被清除时自动取消所有协程fun loadUserData() {viewModelScope.launch { // 在主线程启动_uiState.value = UiState.Loadingtry {// 切换到 IO 线程执行网络请求和数据库操作val userProfile = withContext(Dispatchers.IO) {// 并行执行两个异步任务val userDeferred = async { api.getUser() }val postsDeferred = async { api.getPosts() }UserProfile(userDeferred.await(), postsDeferred.await())}// 回到主线程更新状态_uiState.value = UiState.Success(userProfile)} catch (e: Exception) {// 回到主线程处理错误_uiState.value = UiState.Error(e.message)}}}
}

2. 密封类(Sealed Classes)

密封类用于表示受限的类继承结构,当一个值只能是有限几种类型之一时非常有用,常与 when 表达式结合使用,确保穷举检查。

2.1 定义与特性

sealed class Result<out T> {data class Success<T>(val data: T) : Result<T>()data class Error(val exception: Exception) : Result<Nothing>()object Loading : Result<Nothing>()
}

2.2 模式匹配

fun handleResult(result: Result<String>) {when (result) {is Result.Success -> println("Data: ${result.data}")is Result.Error -> println("Error: ${result.exception}")Result.Loading -> println("Loading...")}
}

2.3 应用场景

UI状态管理(Idle/Processing/Success/Failure)
API响应处理(Success/Error/NetworkError)

3. 内联函数(Inline Functions)

使用 inline 关键字修饰的函数,在编译时会将其函数体直接插入到调用处,可以减少函数调用的开销,尤其适用于接收 Lambda 作为参数的高阶函数,可以避免 Lambda 对象的创建。

3.1 基础用法

// 高阶函数内联优化
inline fun <T> measureTime(block: () -> T): T {val start = System.nanoTime()return block().also { println("Time: ${System.nanoTime() - start}") }
}// 使用示例
val result = measureTime {// 耗时操作
}

3.2 关键字扩展

noinline:禁止内联特定lambda参数
crossinline:禁止lambda内部使用return
reified:具体化泛型类型参数

inline fun <reified T> Activity.openAct() {startActivity(Intent(this, T::class.java))
}

4. 扩展函数(Extension Functions)

4.1 基础定义

// 为String添加反转方法
fun String.reverse(): String {return this.reversed()
}// Android扩展
fun Context.showToast(message: String) {Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

4.2 最佳实践

组织方式:按功能模块分组扩展函数
作用域:优先扩展接口而非具体类
性能优化:避免过度扩展基础类型

5. 类型系统进阶

5.1 空安全

// 安全调用链
val length: Int? = text?.length
val safeLength = text?.length ?: 0// 非空断言
val forcedLength = text!!.length

5.2 泛型系统

协变(out T):生产者角色
逆变(in T):消费者角色
类型投影:Array
Kotlin 使用声明处型变,解决了 Java 通配符 (? extends T, ? super T) 的复杂性问题。

1.out (协变 Covariant):生产者,只能输出(返回)T。Producer<out T> 是 Producer<U> 的子类型,如果 T 是 U 的子类型。类似于 Java 的 ? extends T。interface Producer<out T> {fun produce(): T // T 只出现在 out 位置
}
2.in (逆变 Contravariant):消费者,只能输入(消耗)T。Consumer<in T> 是 Consumer<U> 的子类型,如果 T 是 U 的父类型。类似于 Java 的 ? super T。interface Consumer<in T> {fun consume(item: T) // T 只出现在 in 位置
}

6. 作用域函数(Scope Functions)

Kotlin 提供了几个作用域函数:let, run, with, apply, also。它们的主要目的是在对象的上下文中执行代码块,并且各自有细微的差别(返回值和 this/it 的指代)。

函数对象引用返回值适用场景
letitlambda结果对象为空时跳过操作
runthislambda结果需要计算多个属性时
withthislambda结果配置对象参数
applythis对象自身对象初始化配置
alsoit对象自身对象副作用操作

示例:


// 对象初始化
val user = User().apply {name = "John"age = 30
}// 条件判断
val result = data?.let { process(it) } ?: defaultValue

7. 数据类与解构

允许将一个对象的多个属性或组件一次性赋值给多个变量。

7.1 数据类特性

data class User(val name: String, val age: Int)
自动生成equals()/hashCode()/toString()
支持copy()方法
解构声明:val (name, age) = user

7.2 解构扩展

原理: 编译器会调用对象的 component1(), component2() 等运算符函数。数据类(data class)会自动生成这些函数。

// 为现有类添加解构支持
data class Person(val name: String, val age: Int)fun main() {val person = Person("Alice", 29)// 解构声明:根据主构造函数中声明的属性顺序val (name, age) = personprintln("$name is $age years old") // 输出: Alice is 29 years old// 对于集合也适用(因为 componentN() 函数)val (first, second, third) = listOf("a", "b", "c")println("$first, $second, $third") // 输出: a, b, c
}

8. 委托 (Delegation)

Kotlin 原生支持委托模式,通过 by 关键字实现,可以将一个类的接口实现委托给另一个对象。

8.1 类委托

interface Base {fun print()
}class BaseImpl(val x: Int) : Base {override fun print() { print(x) }
}// Derived 类将 Base 接口的实现委托给 baseObject
class Derived(b: Base) : Base by bfun main() {val b = BaseImpl(10)Derived(b).print() // 输出: 10
}

8.2 属性委托

最常用的是 lazy 和 observable

import kotlin.properties.Delegatesclass Example {// 延迟初始化,第一次访问时才计算val lazyValue: String by lazy {println("computed!")"Hello"}// 可观察属性,值改变时会触发回调var observedValue: String by Delegates.observable("<no name>") {prop, old, new ->println("$old -> $new")}
}fun main() {val e = Example()println(e.lazyValue) // 第一次访问,输出: computed! 然后输出: Helloprintln(e.lazyValue) // 第二次访问,直接输出: Helloe.observedValue = "first" // 输出: <no name> -> firste.observedValue = "second" // 输出: first -> second
}

9.高阶函数 和 Lambda 表达式

高阶函数是将函数用作参数或返回值的函数。Lambda 表达式是定义匿名函数的简洁方式。

// 定义一个高阶函数
fun calculate(x: Int, y: Int, operation: (Int, Int) -> Int): Int {return operation(x, y)
}fun main() {// 使用 Lambda 表达式val addResult = calculate(10, 5) { a, b -> a + b }println(addResult) // 输出: 15// 使用函数引用 (::)val multiplyResult = calculate(10, 5, ::multiplyHelper)println(multiplyResult) // 输出: 50// 另一个例子:使用集合的高阶函数val numbers = listOf(1, 2, 3, 4, 5)val evenSquares = numbers.filter { it % 2 == 0 }   // 过滤偶数.map { it * it }          // 计算平方println(evenSquares) // 输出: [4, 16]
}fun multiplyHelper(a: Int, b: Int) = a * b

要点:

it:如果 Lambda 只有一个参数,可以使用默认名称 it。

最后一个 Lambda:如果函数的最后一个参数是 Lambda,它可以移到括号外面。如果它是唯一参数,括号可以省略。这是 Kotlin DSL 的基础。

()->Unit:表示一个无参数无返回值的函数类型。

10. 高级特性应用

10.1 DSL构建

// RecyclerView DSL示例
recyclerView.build {layoutManager = LinearLayoutManager(context)adapter {itemType<User> {layoutRes = R.layout.item_userbind { holder, user ->holder.name.text = user.name}}}
}

10.2 反射与元编程

完整支持Java反射API
Kotlin反射库:kotlin-reflect 在运行时动态地检查、访问和操作类、对象、属性、函数等。功能强大,但性能开销较大。

import kotlin.reflect.full.*data class Person(val name: String, var age: Int)fun main() {val person = Person("Alice", 29)val kClass = person::class // 获取 KClass 对象// 检查成员kClass.memberProperties.forEach { println(it.name) } // 输出: name, age// 访问属性值val ageProperty = kClass.declaredMemberProperties.find { it.name == "age" }println(ageProperty?.get(person)) // 输出: 29// 调用函数val kFunction = ::Person // 获取构造函数引用val newPerson = kFunction.call("Bob", 30)println(newPerson) // 输出: Person(name=Bob, age=30)
}

11.Flow 数据流处理

Flow 是 Kotlin 协程库中用于处理异步数据流(Asynchronous Streams)的组件。它可以按顺序发射多个值,而不是像 suspend 函数那样只返回单个值。你可以把它想象成一个“异步序列”或“响应式流”,类似于 RxJava 的 Observable 或 Reactor 的 Flux。

11.1创建 Flow

最常用的创建方式是使用 flow { … } 构建器。

import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*// 创建一个简单的 Flow,发射 1 到 3 三个数字
fun simpleFlow(): Flow<Int> = flow {println("Flow started")for (i in 1..3) {delay(1000) // 模拟一个异步操作,比如网络请求emit(i) // 发射一个值到流中}
}suspend fun main() = runBlocking {// 第一次收集:会触发 Flow 的执行println("Calling collect first time...")simpleFlow().collect { value -> println("Collected $value") }// 等待一段时间后再次收集delay(3000)// 第二次收集:会再次触发一个全新的、独立的执行println("Calling collect second time...")simpleFlow().collect { value -> println("Collected again $value") }
}

输出:

Calling collect first time…
Flow started Collected 1 // 等待1秒后
Collected 2 // 再等待1秒后
Collected 3 // 再等待1秒后
Calling collect second
time… Flow started // 再次打印,证明是新的执行
Collected again 1
Collected again 2
Collected again 3

11.2 Flow 的生命周期操作符

Flow 提供了类似集合的操作符,但它们是中间操作符,返回一个新的 Flow,并且大多是冷的。

转换操作符 (Transform Operators)

  • map: 将每个发射的值进行转换。

  • filter: 过滤发射的值。

  • transform: 更通用的转换,可以发射任意次数的值。

suspend fun main() = runBlocking {(1..5).asFlow() // 将集合转换为 Flow.filter { it % 2 == 0 } // 过滤偶数.map { it * it } // 将偶数平方.collect { println(it) } // 收集结果:4, 16
}- 限长操作符 (Size-limiting Operators)
take: 只取前 N 个值,然后取消流的执行。
suspend fun main() = runBlocking {flow {try {emit(1)emit(2)println("This will not print")emit(3) // 因为 take(2),执行到这里之前流已被取消} finally {println("Finally in flow") // 仍然会执行,用于资源清理}}.take(2).collect { println(it) } // 输出: 1, 2, Finally in flow
}
  • 末端操作符 (Terminal Operators)
    末端操作符是挂起函数,它启动流的收集。最常见的末端操作符是 collect。
  • collect: 收集所有发射的值。
  • toList / toSet: 将流收集到集合中。
  • first() / single(): 获取第一个或唯一一个值。
  • reduce / fold: 对流进行聚合操作。
suspend fun main() = runBlocking {val sum = (1..5).asFlow().reduce { accumulator, value -> accumulator + value } // 累加: 1+2+3+4+5println(sum) // 输出: 15
}

11.3流上下文 (Context) 与 flowOn

Flow 的构建器代码默认在收集器所在的协程上下文中运行。如果要改变流发射的上下文(例如,切换到 IO 线程进行网络请求),需要使用 flowOn 操作符。

fun simpleFlow(): Flow<Int> = flow {println("Started in ${Thread.currentThread().name}") // 在 IO 线程执行for (i in 1..3) {delay(100)emit(i)}
}.flowOn(Dispatchers.IO) // 指定上游流的执行上下文suspend fun main() = runBlocking {println("Collecting in ${Thread.currentThread().name}") // 在主线程收集simpleFlow().collect { value ->println("$value collected in ${Thread.currentThread().name}") // 在主线程收集}
}

输出:

text Collecting in main @coroutine#1
Started in DefaultDispatcher-worker-1 @coroutine#2
1 collected in main @coroutine#1
2 collected in main @coroutine#1
3 collected in main @coroutine#1

注意:flowOn 改变了它之前的操作符的上下文。收集操作 collect 仍然在原始的上下文中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/97890.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/97890.shtml
英文地址,请注明出处:http://en.pswp.cn/pingmian/97890.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

9 基于机器学习进行遥感影像参数反演-以随机森林为例

目录 1 读取数据 2 数据预处理 3模型训练 4模型预测 5精度分析 由于回归任务的标签数据获取比较困难,我们这次用水体指数NDWI来模拟作为回归任务的标签,通过随机森林来拟合回归NDWI,其计算公式如下: NDWI = (band3 - band5) / (band3 + band5) 实际情况下需要回归的数…

C++多线程编程:跨线程操作全解析

C中的"线程"通常指单个执行流&#xff08;如std::thread对象&#xff09;&#xff0c;而"多线程"指程序中同时存在多个这样的执行流&#xff0c;并涉及它们的创建、管理和同步。实现跨线程操作的核心在于安全地处理共享数据和线程间通信。 以下是实现跨线程…

【脑电分析系列】第13篇:脑电源定位:从头皮到大脑深处,EEG源定位的原理、算法与可视化

前言脑电信号&#xff08;Electroencephalography, EEG&#xff09;是一种非侵入性的神经成像技术&#xff0c;能够实时捕捉大脑的电活动。然而&#xff0c;头皮上记录到的信号是脑源活动经过头皮、颅骨等介质“模糊”后的投影。想要从这些头皮EEG信号追溯到大脑深处的电活动&a…

MySQL知识笔记

DATE_ADD(date,INTERVAL expr type) date 参数是合法的日期表达式。expr 参数是您希望添加的时间间隔。多查官方手册&#xff01;&#xff01;命令行启动和停止sql服务net start 数据库名&#xff1b; 这是启动服务命令&#xff1b; 例如&#xff1a;net start Mysql56…

2025算法八股——深度学习——MHA MQA GQA

MHA、MQA、GQA 都是深度学习中注意力机制的相关概念&#xff0c;其中 MHA 是标准的多头注意力机制&#xff0c;MQA 和 GQA 则是其优化变体&#xff0c;以下是它们的区别、优缺点介绍&#xff1a;区别MHA&#xff08;多头注意力&#xff09;&#xff1a;是 Transformer 架构的核…

Vue3》》eslint Prettier husky

安装必要的依赖 npm install -D eslint eslint/js vue/eslint-config-prettier prettier eslint-plugin-vue 初始化 ESLint 配置 npm init eslint/config// eslint.config.js // 针对 JavaScript 的 ESLint 配置和规则。保持 JavaScript 代码的一致性和质量 import js from &qu…

Custom SRP - Point and Spot Lights

https://catlikecoding.com/unity/tutorials/custom-srp/point-and-spot-lights/Lights with Limited Influence1 Point Lights1.1 Other Light Data (Point )同方向光一样,我们支持有限数量的 Other Light.尽管场景中可能有很多 Other Lights,可能有超过光源上限的光源时可见的…

hive数据仓库的搭建

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录前言一、内嵌模式二、本地模式三、远程模式前言 HIVE是基于HDFS的数据仓库&#xff0c;要首先搭建好HADOOP的集群才可以正常使用HIVE&#xff0c;HADOOP集运搭建详见…

域名SSL证书免费申请lcjmSSL

.-.lcjmSSL&#xff08;又名“来此加密”&#xff09;是一个提供免费SSL证书申请的一站式平台。它支持单域名、多域名以及泛域名证书申请&#xff0c;且单张证书最高可覆盖100个域名&#xff0c;让您轻松实现全站HTTPS加密。为什么您的网站必须安装SSL证书&#xff1f;数据加密…

“能量逆流泵”:一种基于电容阵与开关矩阵的超高效大功率降压架构

摘要本文提出并验证了一种面向大功率降压应用的革命性电源架构——"能量逆流泵"&#xff08;Energy Inversion Pump, EIP&#xff09;。该架构摒弃了传统Buck转换器中的电感元件&#xff0c;通过高速开关矩阵控制的电容阵列&#xff0c;将高压侧能量以"分时、分…

打造精简高效的 uni-app 网络请求工具

在 uni-app 开发中&#xff0c;网络请求是连接前端与后端的核心桥梁。一个设计良好的请求工具能够显著提升开发效率&#xff0c;减少重复代码。本文将分享一个精简版的 uni-app 网络请求工具实现&#xff0c;它保留了核心功能同时保持了足够的灵活性。设计思路一个优秀的网络请…

【面试场景题】交易流水表高qps写入会有锁等待或死锁问题吗

文章目录一、先明确交易流水表的核心特性二、InnoDB的锁机制在流水表写入场景的表现1. 行锁&#xff08;Record Lock&#xff09;&#xff1a;基本不涉及2. 间隙锁&#xff08;Gap Lock&#xff09;与Next-Key Lock&#xff1a;几乎不触发3. 表锁&#xff1a;仅在极端场景出现三…

项目部署——LAMP、LNMP和LTMJ

前情提要问&#xff1a;如何通过nginx的反向代理&#xff0c;代理多台虚拟主机&#xff08;一台apache服务器上的虚拟主机&#xff09;&#xff1f;1.在nginx的配置文件中&#xff0c;将基于域名的访问改为基于端口的访问&#xff08;nginx.conf&#xff09;upstream daili{ser…

晨曦中,它已劳作:一台有温度的机器人如何重塑我们的洁净日常

清晨六点&#xff0c;城市的轮廓在微光中逐渐清晰。某高端小区的路面上&#xff0c;一台灰色机身、线条流畅的机器正在安静地工作。它绕过停靠的车辆&#xff0c;精准地沿着路缘石前进&#xff0c;吸走落叶与尘土&#xff0c;遇到突然窜出的流浪猫时轻巧避让&#xff0c;仿佛有…

【最新高级版】酷柚易汛生产管理系统v1.2.8 +uniapp全开源+文档教程

酷柚易汛生产管理系统是基于FastAdminThinkPHPLayuiuniapp开发的生产管理系统&#xff0c;帮助企业数字化转型&#xff0c;打造智能工厂&#xff0c;专业为生产企业量身开发的一套完整的生产管理系统。主要包含以下模块&#xff1a;购货模块、生产模块、仓库模块、资料模块&…

40分钟的Docker实战攻略

一&#xff1a;什么是Docker &#xff08;1&#xff09;基本概念 Docker 是一种开源的 容器化平台&#xff0c;用于快速构建、部署和运行应用程序。它通过将应用程序及其依赖项打包到轻量级的、可移植的容器中&#xff0c;实现了环境一致性&#xff0c;解决了“在我机器上能运…

qt使用camke时,采用vcpkg工具链设置OSG的qt模块osgQOpenGLWidget

【免费】osgQOpenGLWidget嵌入qt模块,VS2022使用cmake的方式,工具链vcpkg资源-CSDN下载 CMake中设置 1.查找osg相关的库,同时也会设置对应include的路径 # 检查是否找到 osg find_package(OpenSceneGraph 3.6.5REQUIRED COMPONENTS osgosgUtilosgGAosgViewerosgDBosgAnimatio…

洛谷 P2245 星际导航(kruskal 重构树 + 倍增优化求路径最大边权)

题目链接 题目难度 洛谷上是蓝题&#xff0c;我觉得这道题挺简单的&#xff0c;一眼就看穿了&#xff0c;应该是绿题。 题目解法概括 kruskal 重构树 倍增优化求路径最大边权。 代码 #include <iostream> #include <vector> #include <algorithm> #in…

STM32H743-ARM例程1-IDE环境搭建与调试下载

目录实验平台环境搭建一、Keil MDK集成开发环境1.MDK简介2.MDK5安装3.程序下载与调试二、STM32CubeMX1.STM32CubeMX简介2.JAVA JRE安装3.STM32CubeMX安装4.STM32CubeH7库安装实验平台 硬件&#xff1a;银杏科技GT7000双核心开发板-ARM-STM32H743XIH6&#xff0c;银杏科技iTool…

FPGA学习篇——Verilog学习MUX的实现

PS&#xff1a;目前手上仍然没有板子&#xff0c;按照野火视频的讲解&#xff0c;目前我们只能做到前面六步&#xff08;其实第一步设计规划也是需要看板子的硬件的&#xff0c;但是现在没有板子就完全与野火传授的板子一致来看&#xff09; 首先我们以最简单的2路选择器MUX2_1…