本章重点突破
Java开发者需要跨越的3大认知鸿沟:
- 值类型优先的设计哲学(结构体>类)
- 协议的多维度扩展能力(超越接口)
- 面向协议编程范式(POP)的实践
一、结构体与类的抉择
(1) 基础语法对比
// Java类
public class Vehicle {
private int wheels;
public Vehicle(int wheels) {
this.wheels = wheels;
}
public String description() {
return "Vehicle with " + wheels + " wheels";
}
}
// Swift结构体(默认初始化器)
struct Vehicle {
let wheels: Int // 不可变属性
var color: String = "white" // 默认值
func description() -> String {
return "Vehicle with \(wheels) wheels"
}
}
// 使用差异点
let car = Vehicle(wheels: 4) // 栈分配
car.color = "red" // ?错误:结构体是值类型,let常量不可变
var truck = car // 值拷贝产生新实例
(2) 核心差异表
特性 | Swift结构体 | Swift类 | Java类 |
类型性质 | 值类型(栈分配) | 引用类型(堆分配) | 引用类型(堆分配) |
继承 | 不支持 | 单继承 | 单继承 |
内存管理 | 自动值拷贝 | ARC引用计数 | GC垃圾回收 |
线程安全 | 天然线程安全(独立拷贝) | 需手动同步 | 需手动同步 |
拷贝行为 | 深拷贝 | 浅拷贝 | 浅拷贝 |
推荐使用场景 | 简单数据模型 | 需要继承/共享实例的场景 | 所有对象模型 |
(3) 实战选择策略
// ?优先使用结构体的情况
struct Point { var x, y: Double } // 几何坐标
struct RGB { let r, g, b: UInt8 } // 颜色值
struct Angle { var radians: Double } // 角度值
// 必须使用类的情况
class DatabaseConnection { /* 需要共享资源 */ }
class WindowController: NSObject { /* 需要继承Cocoa类 */ }
class SharedState { /* 需要引用语义 */ }
二、协议(Protocol)的维度突破
(1) 基础协议对比
// Java接口
interface Flyable {
int MAX_HEIGHT = 1000; // 默认public static final
void fly(); // 抽象方法
default void land() { // 默认实现
System.out.println("Landing...");
}
}
// Swift协议(协议扩展)
protocol Flyable {
static var maxHeight: Int { get } // 类型属性要求
func fly()
var altitude: Double { get set } // 属性要求
}
extension Flyable {
func land() { // 协议扩展提供默认实现
print("Landing...")
}
}
struct Drone: Flyable {
static let maxHeight = 500
private(set) var altitude = 0.0 // 外部只读
func fly() {
altitude += 100
}
}
(2) 协议进阶特性
特性1:关联类型(类似Java泛型但更灵活)
protocol Container {
associatedtype Element
var count: Int { get }
mutating func append(_ item: Element)
subscript(i: Int) -> Element { get }
}
struct IntStack: Container {
typealias Element = Int // 显式指定类型
private var elements = [Int]()
mutating func append(_ item: Int) {
elements.append(item)
}
subscript(i: Int) -> Int {
return elements[i]
}
}
特性2:条件化扩展
extension Array: Container where Element: Equatable {
func contains(_ element: Element) -> Bool {
// 只有元素可比较时才获得此方法
return self.contains(element)
}
}
特性3:协议组合
func process(entity: Flyable & CustomStringConvertible) {
// 同时遵守多个协议的对象
entity.fly()
print(entity.description)
}
三、扩展(Extension)的降维打击
(1) 基础扩展对比
// Java工具类模式
public final class StringUtils {
public static String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
}
// 使用方式
String reversed = StringUtils.reverse("hello");
// Swift扩展语法
extension String {
func reversed() -> String {
return String(self.reversed())
}
}
// 使用方式
let reversed = "hello".reversed()
(2) 协议扩展黑科技
// 为所有集合类型添加统计功能
extension Collection where Element: Numeric {
var average: Double? {
guard !isEmpty else { return nil }
return Double(reduce(0, +)) / Double(count)
}
}
let numbers = [1, 2, 3, 4, 5]
numbers.average // 3.0
四、枚举的次元突破
(1) 基础枚举对比
// Java枚举
enum Planet {
MERCURY(3.303e+23),
VENUS(4.869e+24);
private final double mass;
Planet(double mass) {
this.mass = mass;
}
public double getMass() {
return mass;
}
}
// Swift枚举(关联值)
enum Planet {
case mercury(mass: Double)
case venus(mass: Double)
var mass: Double {
switch self {
case .mercury(let mass), .venus(let mass):
return mass
}
}
}
let earth = Planet.mercury(mass: 5.972e24)
(2) 模式匹配实战
enum NetworkResponse {
case success(data: Data, code: Int)
case failure(error: Error, retry: Bool)
}
func handle(response: NetworkResponse) {
switch response {
case .success(let data, 200..<300):
processData(data)
case .failure(let error, true):
attemptRetry()
case .failure(_, false):
showErrorMessage()
}
}
五、错误处理范式迁移
(1) 基础语法对比
// Java受检异常
public class FileParser {
public String readFile(String path) throws IOException {
return Files.readString(Paths.get(path));
}
}
// 使用方式
try {
String content = new FileParser().readFile("test.txt");
} catch (IOException e) {
System.err.println("File error: " + e.getMessage());
}
// Swift错误处理
enum FileError: Error {
case notFound
case permissionDenied
}
struct FileParser {
func readFile(path: String) throws -> String {
guard FileManager.default.fileExists(atPath: path) else {
throw FileError.notFound
}
return try String(contentsOfFile: path)
}
}
// 使用方式
do {
let content = try FileParser().readFile("test.txt")
} catch FileError.notFound {
print("文件不存在")
} catch {
print("未知错误: \(error)")
}
(2) 错误处理进阶技巧
技巧1:Result类型处理异步错误
func fetchData(completion: @escaping (Result) -> Void) {
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
completion(.failure(error))
} else if let data = data {
completion(.success(String(data: data, encoding: .utf8)!))
}
}.resume()
}
// 使用方式
fetchData { result in
switch result {
case .success(let str):
print(str)
case .failure(let error):
print("请求失败: \(error)")
}
}
技巧2:try? 与 try! 的合理使用
let content = try? readFile("test.txt") // 返回可选类型
let sureContent = try! readFile("config.json") // 确定不会出错时使用
六、内存管理哲学差异
(1) ARC vs GC对比表
特性 | Swift ARC | Java GC |
内存释放时机 | 引用计数归零立即释放 | 不定期由GC决定 |
系统开销 | 维护引用计数有固定开销 | 全堆扫描导致停顿 |
可预测性 | 内存释放时间确定 | 释放时间不可控 |
循环引用处理 | 需手动使用weak/unowned | 自动检测并回收 |
线程安全 | 原子操作保证线程安全 | 需要同步机制 |
(2) 循环引用解决方案
class DataLoader {
var onCompleted: (() -> Void)? // 强引用
func load() {
// 模拟异步操作
DispatchQueue.main.asyncAfter(deadline: .now()+1) {
self.onCompleted?() // 循环引用!
}
}
}
// 正确写法(使用weak)
func load() {
DispatchQueue.main.asyncAfter(deadline: .now()+1) { [weak self] in
self?.onCompleted?() // 弱引用打破循环
}
}
// 或者使用unowned(当确定对象一定存在时)
func load() {
DispatchQueue.main.asyncAfter(deadline: .now()+1) { [unowned self] in
self.onCompleted?() // 无主引用
}
}
本章关键练习
- 将Java类改写成Swift结构体,体会值类型特性
- 实现一个支持关联类型的容器协议
- 为现有类型(如Int)添加扩展方法
- 设计带有关联值的枚举处理网络状态
- 使用Result类型重构异步错误处理
- 分析并修复一个循环引用场景
本文暂时没有评论,来添加一个吧(●'◡'●)