【Rust 高級trait】Rust trait的一些高級用法解密

在這里插入圖片描述

?? 歡迎大家來到景天科技苑??

🎈🎈 養成好習慣,先贊后看哦~🎈🎈

🏆 作者簡介:景天科技苑
🏆《頭銜》:大廠架構師,華為云開發者社區專家博主,阿里云開發者社區專家博主,CSDN全棧領域優質創作者,掘金優秀博主,51CTO博客專家等。
🏆《博客》:Rust開發,Python全棧,Golang開發,云原生開發,PyQt5和Tkinter桌面開發,小程序開發,人工智能,js逆向,App逆向,網絡系統安全,數據分析,Django,fastapi,flask等框架,云原生K8S,linux,shell腳本等實操經驗,網站搭建,數據庫等分享。

所屬的專欄:Rust語言通關之路
景天的主頁:景天科技苑

在這里插入圖片描述

文章目錄

  • Rust trait高級用法
    • 一、Trait基礎回顧
      • 1.1 Trait定義與實現
      • 1.2 Trait作為參數
      • 1.3 Trait作為返回類型
    • 二、關聯類型(Associated Types)
    • 三、默認泛型類型參數和運算符重載
    • 四、完全限定語法與消歧義:調用相同名稱的方法
    • 五、父 trait 用于在另一個 trait 中使用某 trait 的功能
    • 六、newtype 模式用于在外部類型上實現外部 trait

Rust trait高級用法

Rust語言中的trait是其類型系統的核心特性之一,它提供了定義共享行為的強大機制。
對于初學者來說,trait類似于其他語言中的"接口",但實際上Rust的trait功能要強大得多。
本文將深入探討trait的高級用法,通過實際案例展示如何充分利用這一特性來編寫靈活、可重用且類型安全的Rust代碼。

一、Trait基礎回顧

在深入高級用法之前,讓我們先簡要回顧trait的基本概念。

1.1 Trait定義與實現

// 定義一個簡單的trait
trait Greet {fn greet(&self) -> String;
}// 為具體類型實現trait
struct Person {name: String,
}impl Greet for Person {fn greet(&self) -> String {format!("Hello, my name is {}", self.name)}
}

1.2 Trait作為參數

fn print_greeting<T: Greet>(item: T) {println!("{}", item.greet());
}

1.3 Trait作為返回類型

fn create_greeter(name: String) -> impl Greet {Person { name }
}

二、關聯類型(Associated Types)

關聯類型是trait定義中的占位符類型,允許在實現trait時指定具體類型。
關聯類型(associated types)是一個將類型占位符與 trait 相關聯的方式,這樣 trait 的方法簽名中就可以使用這些占位符類型。
trait 的實現者會針對特定的實現在這個類型的位置指定相應的具體類型。如此可以定義一個使用多種類型的 trait,直到實現此 trait 時都無需知道這些類型具體是什么。

一個帶有關聯類型的 trait 的例子是標準庫提供的 Iterator trait。它有一個叫做 Item 的關聯類型來替代遍歷的值的類型。

pub trait Iterator {type Item;fn next(&mut self) -> Option<Self::Item>;
}

Item 是一個占位類型,同時 next 方法定義表明它返回 OptionSelf::Item 類型的值。
這個 trait 的實現者會指定 Item 的具體類型,然而不管實現者指定何種類型, next 方法都會返回一個包含了此具體類型值的 Option。

關聯類型看起來像一個類似泛型的概念,因為它允許定義一個函數而不指定其可以處理的類型。那么為什么要使用關聯類型呢?
讓我們通過一個Counter 結構體上實現 Iterator trait 的例子來檢視其中的區別。在如下示例中,指定了 Item 的類型為 u32:

trait Iterator {type Item; // 關聯類型,定義的時候無需知道具體是什么類型fn next(&mut self) -> Option<Self::Item>;
}struct Counter {count: u32,
}impl Iterator for Counter {type Item = u32; // 指定關聯類型的具體類型,實現的時候才需要指定具體類型fn next(&mut self) -> Option<Self::Item> {if self.count < 5 {self.count += 1;Some(self.count)} else {None}}
}fn main() {let mut counter = Counter { count: 0 };while let Some(value) = counter.next() {println!("count: {}", value);}
}

在這里插入圖片描述

為啥不使用泛型?
這類似于泛型。那么為什么 Iterator trait 不像如下示例那樣定義呢?

pub trait Iterator<T> {fn next(&mut self) -> Option<T>;
}

區別在于當使用泛型時,則不得不在每一個實現中標注類型。
這是因為我們也可以實現為 Iterator<String> for Counter,或任何其他類型,這樣就可以有多個 Counter 的 Iterator 的實現。
換句話說,當 trait 有泛型參數時,可以多次實現這個 trait,每次需改變泛型參數的具體類型。
接著當使用 Counter 的 next 方法時,必須提供類型標注來表明希望使用 Iterator 的哪一個實現。

當然,非要通過泛型也可以實現,結構體也設置成泛型

//泛型實現
struct Counter2<T> {count: T,
}trait Iterator2<T> {fn next(&mut self) -> Option<T>;
}impl Iterator2<i32> for Counter2<i32> {fn next(&mut self) -> Option<i32> {if self.count < 5 {self.count += 1;Some(self.count)} else {None}}
}impl Iterator2<f32> for Counter2<f32> {fn next(&mut self) -> Option<f32> {if self.count < 5.0 {self.count += 1.0;Some(self.count)} else {None}}
}impl Iterator2<String> for Counter2<String> {fn next(&mut self) -> Option<String> {if self.count.len() < 5 {self.count.push('a');Some(self.count.clone())} else {None}}
}fn main() {let mut counter2 = Counter2 { count: 0 };while let Some(value) = counter2.next() {println!("count: {}", value);}let mut counter3 = Counter2 { count: 0.0 };while let Some(value) = counter3.next() {println!("count: {}", value);}let mut counter4 = Counter2 { count: String::from("a") };while let Some(value) = counter4.next() {println!("count: {}", value);}
}

在這里插入圖片描述

通過關聯類型,則無需標注類型,因為不能多次實現這個 trait。
對于使用關聯類型的定義,我們只能選擇一次 Item 會是什么類型,因為只能有一個 impl Iterator for Counter。
當調用 Counter 的 next 時不必每次指定我們需要 u32 值的迭代器。

三、默認泛型類型參數和運算符重載

當使用泛型類型參數時,可以為泛型指定一個默認的具體類型。
如果默認類型就足夠的話,這消除了為具體類型實現 trait 的需要。為泛型類型指定默認類型的語法是在聲明泛型類型時使用
<PlaceholderType=ConcreteType>
類似這樣
在這里插入圖片描述

這種情況的一個非常好的例子是用于運算符重載。運算符重載(Operator overloading)是指在特定情況下自定義運算符(比如 +)行為的操作。

Rust 并不允許創建自定義運算符或重載任意運算符,不過 std::ops 中所列出的運算符 和相應的 trait 可以通過實現運算符相關 trait 來重載。
例如,如下示例中展示了如何在 Point 結構體上實現 Add trait 來重載 + 運算符,這樣就可以將兩個 Point 實例相加了:

use std::ops::Add;#[derive(Debug, PartialEq)]
struct Point {x: i32,y: i32,
}//這里不指定Add類型,默認是Self,也就是Point類型
impl Add for Point {type Output = Point; //指定關聯類型為我們定義的Point類型fn add(self, other: Point) -> Point {Point {x: self.x + other.x,y: self.y + other.y,}}
}fn main() {//運用加法println!("{:?}", Point { x: 1, y: 0 } + Point { x: 2, y: 3 });
}

add 方法將兩個 Point 實例的 x 值和 y 值分別相加來創建一個新的 Point。Add trait 有一個叫做 Output 的關聯類型,它用來決定 add 方法的返回值類型。
在這里插入圖片描述

這里默認泛型類型位于 Add trait 中。這里是其定義:
在這里插入圖片描述

這看來應該很熟悉,這是一個帶有一個方法和一個關聯類型的 trait。
比較陌生的部分是尖括號中的 RHS=Self:這個語法叫做 默認類型參數(default type parameters)。
RHS 是一個泛型類型參數(“right hand side” 的縮寫),它用于定義 add 方法中的 rhs 參數的類型。
如果實現 Add trait 時不指定 RHS 的具體類型,RHS 的類型將是默認的 Self 類型,也就是在其上實現 Add 的類型。

當為 Point 實現 Add 時,使用了默認的 RHS,因為我們希望將兩個 Point 實例相加。讓我們看看一個實現 Add trait 時希望自定義 RHS 類型而不是使用默認類型的例子。

這里有兩個存放不同單元值的結構體,Millimeters 和 Meters。我們希望能夠將毫米值與米值相加,并讓 Add 的實現正確處理轉換。
可以為 Millimeters 實現 Add 并以 Meters 作為 RHS,其實就是指定相加的兩個數右邊的類型為Meters
如下示例 所示。

use std::ops::Add;//定義毫米和米
#[derive(Debug)]
struct Millimeters(u32);
#[derive(Debug)]
struct Meters(u32);//為Millimeters實現Add trait
//指定other為Meters類型
impl Add<Meters> for Millimeters {type Output = Millimeters;fn add(self, other: Meters) -> Millimeters {Millimeters(self.0 + other.0 * 1000)}
}fn main() {let m = Millimeters(100);let n = Meters(1);//注意: 這兩個類型相加位置不能顛倒,因為我們為Millimeters實現了Add<Meters>,而不是Add<Millimeters>// let result = n + m; //報錯 cannot add `Millimeters` to `Meters`let result = m + n;println!("Result: {:?}", result);
}

毫米加米,得到毫米
在這里插入圖片描述

為了使 Millimeters 和 Meters 能夠相加,我們指定 impl Add 來設定 RHS 類型參數的值而不是使用默認的 Self。
默認參數類型主要用于如下兩個方面:

  • 擴展類型而不破壞現有代碼。
  • 在大部分用戶都不需要的特定情況進行自定義。

標準庫的 Add trait 就是一個第二個目的例子:大部分時候你會將兩個相似的類型相加,不過它提供了自定義額外行為的能力。
在 Add trait 定義中使用默認類型參數意味著大部分時候無需指定額外的參數。
換句話說,一小部分實現的樣板代碼是不必要的,這樣使用 trait 就更容易了。

第一個目的是相似的,但過程是反過來的:如果需要為現有 trait 增加類型參數,為其提供一個默認類型將允許我們在不破壞現有實現代碼的基礎上擴展 trait 的功能。

四、完全限定語法與消歧義:調用相同名稱的方法

Rust 既不能避免一個 trait 與另一個 trait 擁有相同名稱的方法,也不能阻止為同一類型同時實現這兩個 trait。甚至直接在類型上實現開始已經有的同名方法也是可能的!
不過,當調用這些同名方法時,需要告訴 Rust 我們希望使用哪一個。
考慮如下示例中的代碼,這里定義了 trait Pilot 和 Wizard 都擁有方法 fly。
接著在一個本身已經實現了名為 fly 方法的類型 Human 上實現這兩個 trait。每一個 fly 方法都進行了不同的操作:
對于方法的完全限定調用

trait Pilot {fn fly(&self);
}trait Wizard {fn fly(&self);
}struct Human;impl Pilot for Human {fn fly(&self) {println!("This is your captain speaking.");}
}impl Wizard for Human {fn fly(&self) {println!("Up!");}
}impl Human {fn fly(&self) {println!("*waving arms furiously*");}
}//當調用 Human 實例的 fly 時,編譯器默認調用直接實現在類型上的方法
fn main() {let person = Human;//human實例直接調用fly方法,會調用impl Human中的fly方法,而不是Pilot和Wizard中的fly方法person.fly();
}

這表明 Rust 調用了直接實現在 Human 上的 fly 方法。
在這里插入圖片描述

完全限定語法調用具體的方法
為了能夠調用 Pilot trait 或 Wizard trait 的 fly 方法,我們需要使用更明顯的語法以便能指定我們指的是哪個 fly 方法。這個語法展示在如下示例中:

fn main() {let person = Human;//human實例直接調用fly方法,會調用impl Human中的fly方法,而不是Pilot和Wizard中的fly方法person.fly();//使用完全限定語法來調用Pilot和Wizard中的fly方法Pilot::fly(&person);Wizard::fly(&person);
}

在方法名前指定 trait 名向 Rust 澄清了我們希望調用哪個 fly 實現。也可以選擇寫成 Human::fly(&person),這等同于person.fly(),不過如果無需消歧義的話這么寫就有點長了。
在這里插入圖片描述

對于關聯函數的完全限定調用(沒有self參數):
因為 fly 方法獲取一個 self 參數,如果有兩個 類型 都實現了同一 trait,Rust 可以根據 self 的類型計算出應該使用哪一個 trait 實現。
然而,關聯函數是 trait 的一部分,但沒有 self 參數。
當同一作用域的兩個類型實現了同一 trait,Rust 就不能計算出我們期望的是哪一個類型,除非使用 完全限定語法(fully qualified syntax)。
例如,如下示例中的 Animal trait 來說,它有關聯函數 baby_name,結構體 Dog 實現了 Animal,同時有關聯函數 baby_name 直接定義于 Dog 之上:

trait Animal {fn baby_name() -> String;
}struct Dog;impl Dog {fn baby_name() -> String {String::from("Spot")}
}impl Animal for Dog {fn baby_name() -> String {String::from("puppy")}
}fn main() {println!("A baby dog is called {}", Dog::baby_name());
}

這段代碼用于一個動物收容所,他們將所有的小狗起名為 Spot,這實現為定義于 Dog 之上的關聯函數 baby_name。
Dog 類型還實現了 Animal trait,它描述了所有動物的共有的特征。小狗被稱為 puppy,這表現為 Dog 的 Animal trait 實現中與 Animal trait 相關聯的函數 baby_name。

在 main 調用了 Dog::baby_name 函數,它直接調用了定義于 Dog 之上的關聯函數。這段代碼會打印出:
在這里插入圖片描述

這并不是我們需要的。我們希望調用的是 Dog 上 Animal trait 實現那部分的 baby_name 函數,這樣能夠打印出 A baby dog is called puppy。
如果將 main 改為在方法前面加上trait的方式,則會得到一個編譯錯誤:
因為 Animal::baby_name 是關聯函數而不是方法,因此它沒有 self 參數,Rust 無法計算出所需的是哪一個 Animal::baby_name 實現。我們會得到這個編譯錯誤:
在這里插入圖片描述

為了消歧義并告訴 Rust 我們希望使用的是 Dog 的 Animal 實現,需要使用 完全限定語法,這是調用函數時最為明確的方式。如下示例展示了如何使用完全限定語法:

fn main() {//使用完全限定語法來調用Dog實現Animal trait的baby_name方法println!("A baby dog is called {}", <Dog as Animal>::baby_name());
}

我們在尖括號中向 Rust 提供了類型標注,并通過在此函數調用中將 Dog 類型當作 Animal 對待,來指定希望調用的是 Dog 上 Animal trait 實現中的 baby_name 函數。
現在這段代碼會打印出我們期望的數據:
在這里插入圖片描述

通常,完全限定語法定義為:

<Type as Trait>::function(receiver_if_method, next_arg, ...);

對于關聯函數,其沒有一個 receiver,故只會有其他參數的列表。可以選擇在任何函數或方法調用處使用完全限定語法。
然而,允許省略任何 Rust 能夠從程序中的其他信息中計算出的部分。只有當存在多個同名實現而 Rust 需要幫助以便知道我們希望調用哪個實現時,才需要使用這個較為冗長的語法。

五、父 trait 用于在另一個 trait 中使用某 trait 的功能

有時我們可能會需要某個 trait 使用另一個 trait 的功能。
在這種情況下,需要能夠依賴相關的 trait 也被實現。這個所需的 trait 是我們實現的 trait 的 父(超) trait(supertrait)。
語法:
trait1: trait2
trait1就是子trait, trait2是父trait
要求實現子 trait 的類型必須實現所有 supertrait。在 Rust 中,被繼承的 trait 稱為 supertrait。實現子 trait 時,必須已經實現了所有 supertrait

例如我們希望創建一個帶有 outline_print 方法的 trait OutlinePrint,它會打印出帶有星號框的值。
也就是說,如果 Point 實現了 Display 并返回 (x, y),調用以 1 作為 x 和 3 作為 y 的 Point 實例的 outline_print 會顯示如下:
實現Display,只需要實現fmt方法
在這里插入圖片描述

//子trait父trait
use std::fmt::Display;//自定義OutlinePrint trait 繼承Display trait,要求實現Display trait
trait OutlinePrint: Display {fn outline_print(&self) {let output = self.to_string();let len = output.len();println!("{}", "*".repeat(len + 4));println!("*{}*", " ".repeat(len + 2));println!("* {} *", output);println!("*{}*", " ".repeat(len + 2));println!("{}", "*".repeat(len + 4));}
}struct Point {x: i32,y: i32,
}//為Point實現Display trait,只需要實現fmt方法
impl Display for Point {fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {write!(f, "({}, {})", self.x, self.y)}
}impl OutlinePrint for Point {}fn main() {let p = Point { x: 1, y: 3 };p.outline_print();
}

在這里插入圖片描述

六、newtype 模式用于在外部類型上實現外部 trait

之前我們提到了孤兒規則(orphan rule),它說明只要 trait 或類型對于當前 crate 在本地的話就可以在此類型上實現該 trait。
一個繞開這個限制的方法是使用 newtype 模式(newtype pattern),它涉及到在一個元組結構體中創建一個新類型。
這個元組結構體帶有一個字段作為希望實現 trait 的類型的簡單封裝。
接著這個封裝類型對于 crate 是本地的,這樣就可以在這個封裝上實現 trait。
Newtype 是一個源自 Haskell 編程語言的概念。使用這個模式沒有運行時性能消耗,這個封裝類型在編譯時就被省略了。

例如,如果想要在 Vec<T> 上實現 Display,而孤兒規則阻止我們直接這么做,因為 Display trait 和 Vec<T> 都定義于我們的 crate 之外。
可以創建一個包含 Vec<T> 實例的 Wrapper 結構體,接著可以如下示例 那樣在 Wrapper 上實現 Display 并使用 Vec<T> 的值:

//外部類型實現外部trait
use std::fmt;//創建一個包含外部類型Vec的元組結構體Wrapper
struct Wrapper(Vec<String>);//為Wrapper實現Display trait
impl fmt::Display for Wrapper {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {//以逗號相連成[]包裹的字符串write!(f, "[{}]", self.0.join(", "))}
}fn main() {let w = Wrapper(vec![String::from("hello"), String::from("world")]);println!("w = {}", w);
}

在這里插入圖片描述

Display 的實現使用 self.0 來訪問其內部的 Vec,因為 Wrapper 是元組結構體而 Vec 是結構體總位于索引 0 的項。接著就可以使用 Wrapper 中 Display 的功能了。
此方法的缺點是,因為 Wrapper 是一個新類型,它沒有定義于其值之上的方法;
必須直接在 Wrapper 上實現 Vec 的所有方法,這樣就可以代理到self.0 上 —— 這就允許我們完全像 Vec 那樣對待 Wrapper。
如果希望新類型擁有其內部類型的每一個方法,為封裝類型實現 Deref trait 并返回其內部類型是一種解決方案。
如果不希望封裝類型擁有所有內部類型的方法 —— 比如為了限制封裝類型的行為 —— 則必須只自行實現所需的方法。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/908098.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/908098.shtml
英文地址,請注明出處:http://en.pswp.cn/news/908098.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

聯想電腦護眼衛士與系統顏色配置(X-Rite)沖突 | 顯示設置頻繁變換色階 - 解決方案

聯想電腦護眼衛士與系統顏色配置X-Rite沖突 | 顯示設置頻繁變換色階 - 解決方案 前言方案1&#xff1a;解決聯想護眼衛士方案2&#xff1a;解決系統顏色配置(X-Rite) 前言 自帶X-Rite軟件的聯想電腦&#xff08;以拯救者Y9000P&#xff0c;Win11系統為例&#xff09;&#xff…

MySQL中SELECT查詢的執行順序

MySQL中SELECT查詢的執行順序 在日常的數據庫開發中&#xff0c;我們經常會寫各種復雜的SELECT查詢語句。然而&#xff0c;很多開發者對于MySQL實際執行這些查詢的順序并不完全了解。理解查詢的執行順序不僅有助于編寫更高效的SQL語句&#xff0c;還能幫助我們更好地優化查詢性…

es 的字段類型(text和keyword)

Text 當一個字段是要被全文檢索時&#xff0c;比如 Email 內容、產品描述&#xff0c;這些字段應該使用 text 類型。設置 text 類型以后&#xff0c;字段內容會被分析&#xff0c;在生成倒排索引之前&#xff0c;字符串會被分析器分詞。text類型的字段不用于排序&#xff0c;很…

MySQL安裝及啟用詳細教程(Windows版)

MySQL安裝及啟用詳細教程&#xff08;Windows版&#xff09; &#x1f4cb; 概述 本文檔將詳細介紹MySQL數據庫在Windows系統下的下載、安裝、配置和啟用過程。 &#x1f4e5; MySQL下載 官方下載地址 官方網站: https://dev.mysql.com/downloads/社區版本: https://dev.my…

Linux下使用nmcli連接網絡

Linux下使用nmcli連接網絡 介紹 在使用ubuntu系統的時候&#xff0c;有時候不方便使用桌面&#xff0c;使用ssh遠程連接&#xff0c;可能需要使用nmcli命令來連接網絡。本文將介紹如何使用nmcli命令連接網絡。nmcli 是 NetworkManager 的命令行工具&#xff0c;用于管理網絡連…

Python----循環神經網絡(BiLSTM:雙向長短時記憶網絡)

一、LSTM 與 BiLSTM對比 1.1、LSTM LSTM&#xff08;長短期記憶網絡&#xff09; 是一種改進的循環神經網絡&#xff08;RNN&#xff09;&#xff0c;專門解決傳統RNN難以學習長期依賴的問題。它通過遺忘門、輸入門和輸出門來控制信息的流動&#xff0c;保留重要信息并丟棄無關…

U盤掛載Linux

在 只能使用 Telnet 的情況下&#xff0c;如果希望通過 U盤 傳輸文件到 Linux 系統&#xff0c;可以按照以下步驟操作&#xff1a; &#x1f4cc; 前提條件 U盤已插入 Linux 主機的 USB 接口。Linux 主機支持自動掛載 U盤&#xff08;大多數現代發行版默認支持&#xff09;。T…

QuickBASIC QB64 支持 64 位系統和跨平臺Linux/MAC OS

QuickBASIC 的現代繼任者 QB64 已發展成為一個功能強大的開源項目&#xff0c;支持 64 位系統和跨平臺開發。以下是詳細介紹&#xff1a; 項目首頁 - QB64pe:The QB64 Phoenix Edition Repository - GitCode https://gitcode.com/gh_mirrors/qb/QB64pe 1. QB64 概述 官網&am…

【C++高級主題】命令空間(五):類、命名空間和作用域

目錄 一、實參相關的查找&#xff08;ADL&#xff09;&#xff1a;函數調用的 “智能搜索” 1.1 ADL 的核心規則 1.2 ADL 的觸發條件 1.3 ADL 的典型應用場景 1.4 ADL 的潛在風險與規避 二、隱式友元聲明&#xff1a;類與命名空間的 “私密通道” 2.1 友元聲明的基本規則…

免費開源Umi-OCR,離線使用,批量精準!

Umi-OCR&#xff08;Windows端&#xff09; Umi-OCR 是一款在 GitHub 上開源的免費 OCR 識別軟件&#xff0c;它最大的亮點就是免費、開源、支持批量處理&#xff0c;而且識別準確度很高。這款軟件不需要聯網就能用&#xff0c;非常值得推薦&#xff01; 在 OCR 識別功能方面&…

深入剖析 Docker 容器化原理與實戰應用,開啟技術新征程!

文章目錄 前言一、為什么 是Docker &#xff1f;二、Docker 容器化原理分析2.1 鏡像&#xff08;Image&#xff09;2.2 容器&#xff08;Container&#xff09;2.3 倉庫&#xff08;Registry&#xff09; 三、Docker 容器化實踐3.1 Docker安裝3.2 創建一個 Docker 鏡像3.3 運行…

黑馬程序員TypeScript課程筆記—class篇

class的基本使用 class的構造函數&#xff08;實現實例屬性的初始化&#xff09; 在使用構造函數的時候&#xff0c;小括號的后面不要指定類型&#xff0c;否則就會報錯&#xff0c;因為構造函數沒有返回值 class實例方法 class繼承&#xff08;extends&#xff09; class繼承…

PDF.js無法顯示數字簽名

問題 pdfjs加載pdf文件時無法顯示數字簽名 PDF.js 從 v2.9.359 版本開始正式支持數字簽名的渲染與顯示&#xff0c;此前版本需通過修改源代碼實現基礎兼容。 建議升級pdfjs組件大于等于v2.9.359 pdfjs歷史版本&#xff1a;https://github.com/mozilla/pdf.js/releases pdfjs…

解決VS Code誤報Java問題的終極方法

使用vscode寫java&#xff0c;發現很多Problems&#xff0c;如下圖&#xff0c;實際上并沒有問題&#xff0c;是誤報&#xff0c;怎么解決&#xff1f; 解決方案&#xff1a;disable下面這個插件&#xff0c;它和vscode-java插件沖突了導致。

【WPF】從普通 ItemsControl 到支持篩選的 ItemsControl:深入掌握 CollectionViewSource 用法

? 從普通 ItemsControl 到支持篩選的 ItemsControl&#xff1a;深入掌握 CollectionViewSource 用法 在日常 WPF 開發中&#xff0c;我們經常需要對數據進行篩選、排序、分組等操作&#xff0c;而原生的 ItemsControl 并不直接支持這些功能。本文將介紹如何通過 CollectionVi…

Mybatis Plus JSqlParser解析sql語句及JSqlParser安裝步驟

MyBatis Plus與JSqlParser&#xff1a;SQL語句解析與實戰指南 在現代Java開發中&#xff0c;SQL解析和動態SQL生成是數據庫操作中不可或缺的一部分。MyBatis Plus作為MyBatis的增強工具&#xff0c;通過JSqlParser庫實現了對SQL語句的深度解析和修改能力。本文將詳細介紹如何在…

學習路之PHP--easyswoole使用視圖和模板

學習路之PHP--easyswoole使用視圖和模板 一、安裝依賴插件二、 實現渲染引擎三、注冊渲染引擎四、測試調用寫的模板五、優化六、最后補充 一、安裝依賴插件 composer require easyswoole/template:1.1.* composer require topthink/think-template相關版本&#xff1a; "…

設計模式——享元設計模式(結構型)

摘要 享元設計模式是一種結構型設計模式&#xff0c;旨在通過共享對象減少內存占用和提升性能。其核心思想是將對象狀態分為內部狀態&#xff08;可共享&#xff09;和外部狀態&#xff08;不可共享&#xff09;&#xff0c;并通過享元工廠管理共享對象池。享元模式包含抽象享…

互聯網大廠Java求職面試:云原生微服務架構設計與AI大模型集成實戰

互聯網大廠Java求職面試&#xff1a;云原生微服務架構設計與AI大模型集成實戰 面試場景設定 人物設定&#xff1a; 李明&#xff08;技術總監&#xff09;&#xff1a;擁有15年分布式系統架構經驗&#xff0c;主導過多個億級用戶系統的重構&#xff0c;對云原生和AI融合有深…

nginx+tomcat動靜分離、負載均衡

一、理論 nginx用于處理靜態頁面以及做調度器&#xff0c;tomcat用于處理動態頁面 lvs&#xff08;四層&#xff09; 輪詢&#xff08;rr&#xff09; 加權輪詢&#xff08;wrr&#xff09; 最小連接&#xff08;lc&#xff09; 加權最小連接&#xff08;wlc&#xff09; ngi…