二十、泛型(2)

本章概要

  • 泛型接口
  • 泛型方法
    • 变长参数和泛型方法
    • 一个泛型的 Supplier
    • 简化元组的使用
    • 一个 Set 工具

泛型接口

泛型也可以应用于接口。例如 生成器,这是一种专门负责创建对象的类。实际上,这是 工厂方法 设计模式的一种应用。不过,当使用生成器创建新的对象时,它不需要任何参数,而工厂方法一般需要参数。生成器无需额外的信息就知道如何创建新对象。

一般而言,一个生成器只定义一个方法,用于创建对象。例如 java.util.function 类库中的 Supplier 就是一个生成器,调用其 get() 获取对象。get() 是泛型方法,返回值为类型参数 T

为了演示 Supplier,我们需要定义几个类。下面是个咖啡相关的继承体系:

Coffee.java

public class Coffee {private static long counter = 0;private final long id = counter++;@Overridepublic String toString() {return getClass().getSimpleName() + " " + id;}
}

Latte.java

public class Latte extends Coffee {
}

Mocha.java

public class Mocha extends Coffee {
}

Cappuccino.java

public class Cappuccino extends Coffee {
}

Americano.java

public class Americano extends Coffee {
}

Breve.java

public class Breve extends Coffee {
}

现在,我们可以编写一个类,实现 Supplier<Coffee> 接口,它能够随机生成不同类型的 Coffee 对象:

import java.util.*;
import java.util.function.*;
import java.util.stream.*;public class CoffeeSupplierimplements Supplier<Coffee>, Iterable<Coffee> {private Class<?>[] types = {Latte.class, Mocha.class,Cappuccino.class, Americano.class, Breve.class};private static Random rand = new Random(47);public CoffeeSupplier() {}// For iteration:private int size = 0;public CoffeeSupplier(int sz) {size = sz;}@Overridepublic Coffee get() {try {return (Coffee) types[rand.nextInt(types.length)].newInstance();} catch (InstantiationException | IllegalAccessException e) {throw new RuntimeException(e);}}class CoffeeIterator implements Iterator<Coffee> {int count = size;@Overridepublic boolean hasNext() {return count > 0;}@Overridepublic Coffee next() {count--;return CoffeeSupplier.this.get();}@Overridepublic void remove() {throw new UnsupportedOperationException();}}@Overridepublic Iterator<Coffee> iterator() {return new CoffeeIterator();}public static void main(String[] args) {Stream.generate(new CoffeeSupplier()).limit(5).forEach(System.out::println);for (Coffee c : new CoffeeSupplier(5)) {System.out.println(c);}}
}

输出结果:

在这里插入图片描述

参数化的 Supplier 接口确保 get() 返回值是参数的类型。CoffeeSupplier 同时还实现了 Iterable 接口,所以能用于 for-in 语句。不过,它还需要知道何时终止循环,这正是第二个构造函数的作用。

下面是另一个实现 Supplier<T> 接口的例子,它负责生成 Fibonacci 数列:

import java.util.function.*;
import java.util.stream.*;public class Fibonacci implements Supplier<Integer> {private int count = 0;@Overridepublic Integer get() {return fib(count++);}private int fib(int n) {if (n < 2) {return 1;}return fib(n - 2) + fib(n - 1);}public static void main(String[] args) {Stream.generate(new Fibonacci()).limit(18).map(n -> n + " ").forEach(System.out::print);}
}

输出结果:

在这里插入图片描述

虽然我们在 Fibonacci 类的里里外外使用的都是 int 类型,但是其参数类型却是 Integer。这个例子引出了 Java 泛型的一个局限性:基本类型无法作为类型参数。不过 Java 5 具备自动装箱和拆箱的功能,可以很方便地在基本类型和相应的包装类之间进行转换。通过这个例子中 Fibonacci 类对 int 的使用,我们已经看到了这种效果。

如果还想更进一步,编写一个实现了 IterableFibnoacci 生成器。我们的一个选择是重写这个类,令其实现 Iterable 接口。不过,你并不是总能拥有源代码的控制权,并且,除非必须这么做,否则,我们也不愿意重写一个类。而且我们还有另一种选择,就是创建一个 适配器 (Adapter) 来实现所需的接口,我们在前面介绍过这个设计模式。

有多种方法可以实现适配器。例如,可以通过继承来创建适配器类:

import java.util.*;public class IterableFibonacciextends Fibonacci implements Iterable<Integer> {private int n;public IterableFibonacci(int count) {n = count;}@Overridepublic Iterator<Integer> iterator() {return new Iterator<Integer>() {@Overridepublic boolean hasNext() {return n > 0;}@Overridepublic Integer next() {n--;return IterableFibonacci.this.get();}@Overridepublic void remove() { // Not implementedthrow new UnsupportedOperationException();}};}public static void main(String[] args) {for (int i : new IterableFibonacci(18)) {System.out.print(i + " ");}}
}

输出结果:

在这里插入图片描述

for-in 语句中使用 IterableFibonacci,必须在构造函数中提供一个边界值,这样 hasNext() 才知道何时返回 false,结束循环。

泛型方法

到目前为止,我们已经研究了参数化整个类。其实还可以参数化类中的方法。类本身可能是泛型的,也可能不是,不过这与它的方法是否是泛型的并没有什么关系。

泛型方法独立于类而改变方法。作为准则,请“尽可能”使用泛型方法。通常将单个方法泛型化要比将整个类泛型化更清晰易懂。

如果方法是 static 的,则无法访问该类的泛型类型参数,因此,如果使用了泛型类型参数,则它必须是泛型方法。

要定义泛型方法,请将泛型参数列表放置在返回值之前,如下所示:

public class GenericMethods {public <T> void f(T x) {System.out.println(x.getClass().getName());}public static void main(String[] args) {GenericMethods gm = new GenericMethods();gm.f("");gm.f(1);gm.f(1.0);gm.f(1.0F);gm.f('c');gm.f(gm);}
}

在这里插入图片描述

尽管可以同时对类及其方法进行参数化,但这里未将 GenericMethods 类参数化。只有方法 f() 具有类型参数,该参数由方法返回类型之前的参数列表指示。

对于泛型类,必须在实例化该类时指定类型参数。使用泛型方法时,通常不需要指定参数类型,因为编译器会找出这些类型。 这称为 类型参数推断。因此,对 f() 的调用看起来像普通的方法调用,并且 f() 看起来像被重载了无数次一样。它甚至会接受 GenericMethods 类型的参数。

如果使用基本类型调用 f() ,自动装箱就开始起作用,自动将基本类型包装在它们对应的包装类型中。

变长参数和泛型方法

泛型方法和变长参数列表可以很好地共存:

import java.util.ArrayList;
import java.util.List;public class GenericVarargs {@SafeVarargspublic static <T> List<T> makeList(T... args) {List<T> result = new ArrayList<>();for (T item : args)result.add(item);return result;}public static void main(String[] args) {List<String> ls = makeList("A");System.out.println(ls);ls = makeList("A", "B", "C");System.out.println(ls);ls = makeList("ABCDEFFHIJKLMNOPQRSTUVWXYZ".split(""));System.out.println(ls);}
}

在这里插入图片描述

此处显示的 makeList() 方法产生的功能与标准库的 java.util.Arrays.asList() 方法相同。

@SafeVarargs 注解保证我们不会对变长参数列表进行任何修改,这是正确的,因为我们只从中读取。如果没有此注解,编译器将无法知道这些并会发出警告。

一个泛型的 Supplier

这是一个为任意具有无参构造方法的类生成 Supplier 的类。为了减少键入,它还包括一个用于生成 BasicSupplier 的泛型方法:

import java.util.function.Supplier;public class BasicSupplier<T> implements Supplier<T> {private Class<T> type;public BasicSupplier(Class<T> type) {this.type = type;}@Overridepublic T get() {try {// Assumes type is a public class:return type.newInstance();} catch (InstantiationException |IllegalAccessException e) {throw new RuntimeException(e);}}// Produce a default Supplier from a type token:public static <T> Supplier<T> create(Class<T> type) {return new BasicSupplier<>(type);}
}

此类提供了产生以下对象的基本实现:

  1. public 的。 因为 BasicSupplier 在单独的包中,所以相关的类必须具有 public 权限,而不仅仅是包级访问权限。
  2. 具有无参构造方法。要创建一个这样的 BasicSupplier 对象,请调用 create() 方法,并将要生成类型的类型令牌传递给它。通用的 create() 方法提供了 BasicSupplier.create(MyType.class) 这种较简洁的语法来代替较笨拙的 new BasicSupplier <MyType>(MyType.class)

例如,这是一个具有无参构造方法的简单类:

public class CountedObject {private static long counter = 0;private final long id = counter++;public long id() {return id;}@Overridepublic String toString() {return "CountedObject " + id;}
}

CountedObject 类可以跟踪自身创建了多少个实例,并通过 toString() 报告这些实例的数量。 BasicSupplier 可以轻松地为 CountedObject 创建 Supplier

import java.util.stream.Stream;public class BasicSupplierDemo {public static void main(String[] args) {Stream.generate(BasicSupplier.create(CountedObject.class)).limit(5).forEach(System.out::println);}
}

在这里插入图片描述

泛型方法减少了产生 Supplier 对象所需的代码量。 Java 泛型强制传递 Class 对象,以便在 create() 方法中将其用于类型推断。

简化元组的使用

使用类型参数推断和静态导入,我们将把早期的元组重写为更通用的库。在这里,我们使用重载的静态方法创建元组:

Tuple.java

public class Tuple {public static <A, B> Tuple2<A, B> tuple(A a, B b) {return new Tuple2<>(a, b);}public static <A, B, C> Tuple3<A, B, C>tuple(A a, B b, C c) {return new Tuple3<>(a, b, c);}public static <A, B, C, D> Tuple4<A, B, C, D>tuple(A a, B b, C c, D d) {return new Tuple4<>(a, b, c, d);}public static <A, B, C, D, E>Tuple5<A, B, C, D, E> tuple(A a, B b, C c, D d, E e) {return new Tuple5<>(a, b, c, d, e);}
}

Tuple2.java

public class Tuple2<A, B> {public final A a1;public final B a2;public Tuple2(A a, B b) {a1 = a;a2 = b;}public String rep() {return a1 + ", " + a2;}@Overridepublic String toString() {return "(" + rep() + ")";}
}

Tuple3.java

public class Tuple3<A, B, C> extends Tuple2<A, B> {public final C a3;public Tuple3(A a, B b, C c) {super(a, b);a3 = c;}@Overridepublic String rep() {return super.rep() + ", " + a3;}
}

Tuple4.java

public class Tuple4<A, B, C, D>extends Tuple3<A, B, C> {public final D a4;public Tuple4(A a, B b, C c, D d) {super(a, b, c);a4 = d;}@Overridepublic String rep() {return super.rep() + ", " + a4;}
}

Tuple5.java

public class Tuple5<A, B, C, D, E>extends Tuple4<A, B, C, D> {public final E a5;public Tuple5(A a, B b, C c, D d, E e) {super(a, b, c, d);a5 = e;}@Overridepublic String rep() {return super.rep() + ", " + a5;}
}

我们修改 TupleTest.java 来测试 Tuple.java :

import static com.example.test.Tuple.tuple;public class TupleTest2 {static Tuple2<String, Integer> f() {return tuple("hi", 47);}static Tuple2 f2() {return tuple("hi", 47);}static Tuple3<Amphibian, String, Integer> g() {return tuple(new Amphibian(), "hi", 47);}static Tuple4<Vehicle, Amphibian, String, Integer> h() {return tuple(new Vehicle(), new Amphibian(), "hi", 47);}static Tuple5<Vehicle, Amphibian,String, Integer, Double> k() {return tuple(new Vehicle(), new Amphibian(),"hi", 47, 11.1);}public static void main(String[] args) {Tuple2<String, Integer> ttsi = f();System.out.println(ttsi);System.out.println(f2());System.out.println(g());System.out.println(h());System.out.println(k());}
}

Americano.java

public class Americano extends Coffee {
}

Vehicle.java

public class Vehicle {
}

在这里插入图片描述

请注意,f() 返回一个参数化的 Tuple2 对象,而 f2() 返回一个未参数化的 Tuple2 对象。编译器不会在这里警告 f2() ,因为返回值未以参数化方式使用。从某种意义上说,它被“向上转型”为一个未参数化的 Tuple2 。 但是,如果尝试将 f2() 的结果放入到参数化的 Tuple2 中,则编译器将发出警告。

一个 Set 工具

对于泛型方法的另一个示例,请考虑由 Set 表示的数学关系。这些被方便地定义为可用于所有不同类型的泛型方法:

import java.util.HashSet;
import java.util.Set;public class Sets {public static <T> Set<T> union(Set<T> a, Set<T> b) {Set<T> result = new HashSet<>(a);result.addAll(b);return result;}public static <T>Set<T> intersection(Set<T> a, Set<T> b) {Set<T> result = new HashSet<>(a);result.retainAll(b);return result;}// Subtract subset from superset:public static <T> Set<T>difference(Set<T> superset, Set<T> subset) {Set<T> result = new HashSet<>(superset);result.removeAll(subset);return result;}// Reflexive--everything not in the intersection:public static <T> Set<T> complement(Set<T> a, Set<T> b) {return difference(union(a, b), intersection(a, b));}
}

前三个方法通过将第一个参数的引用复制到新的 HashSet 对象中来复制第一个参数,因此不会直接修改参数集合。因此,返回值是一个新的 Set 对象。

这四种方法代表数学集合操作: union() 返回一个包含两个参数并集的 Setintersection() 返回一个包含两个参数集合交集的 Setdifference()superset 中减去 subset 的元素 ,而 complement() 返回所有不在交集中的元素的 Set。作为显示这些方法效果的简单示例的一部分,下面是一个包含不同水彩名称的 enum

public enum Watercolors {ZINC, LEMON_YELLOW, MEDIUM_YELLOW, DEEP_YELLOW,ORANGE, BRILLIANT_RED, CRIMSON, MAGENTA,ROSE_MADDER, VIOLET, CERULEAN_BLUE_HUE,PHTHALO_BLUE, ULTRAMARINE, COBALT_BLUE_HUE,PERMANENT_GREEN, VIRIDIAN_HUE, SAP_GREEN,YELLOW_OCHRE, BURNT_SIENNA, RAW_UMBER,BURNT_UMBER, PAYNES_GRAY, IVORY_BLACK
}

为了方便起见(不必全限定所有名称),将其静态导入到以下示例中。本示例使用 EnumSet 轻松从 enum 中创建 Set 。在这里,静态方法 EnumSet.range() 要求提供所要在结果 Set 中创建的元素范围的第一个和最后一个元素:

import java.util.EnumSet;
import java.util.Set;import static com.example.test.Sets.*;
import static com.example.test.Watercolors.*;public class WatercolorSets {public static void main(String[] args) {Set<Watercolors> set1 =EnumSet.range(BRILLIANT_RED, VIRIDIAN_HUE);Set<Watercolors> set2 =EnumSet.range(CERULEAN_BLUE_HUE, BURNT_UMBER);System.out.println("set1: " + set1);System.out.println("set2: " + set2);System.out.println("union(set1, set2): " + union(set1, set2));Set<Watercolors> subset = intersection(set1, set2);System.out.println("intersection(set1, set2): " + subset);System.out.println("difference(set1, subset): " +difference(set1, subset));System.out.println("difference(set2, subset): " +difference(set2, subset));System.out.println("complement(set1, set2): " +complement(set1, set2));}
}

在这里插入图片描述

接下来的例子使用 Sets.difference() 方法来展示 java.util 包中各种 CollectionMap 类之间的方法差异:

import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;public class CollectionMethodDifferences {static Set<String> methodSet(Class<?> type) {return Arrays.stream(type.getMethods()).map(Method::getName).collect(Collectors.toCollection(TreeSet::new));}static void interfaces(Class<?> type) {System.out.print("Interfaces in " +type.getSimpleName() + ": ");System.out.println(Arrays.stream(type.getInterfaces()).map(Class::getSimpleName).collect(Collectors.toList()));}static Set<String> object = methodSet(Object.class);static {object.add("clone");}static voiddifference(Class<?> superset, Class<?> subset) {System.out.print(superset.getSimpleName() +" extends " + subset.getSimpleName() +", adds: ");Set<String> comp = Sets.difference(methodSet(superset), methodSet(subset));comp.removeAll(object); // Ignore 'Object' methodsSystem.out.println(comp);interfaces(superset);}public static void main(String[] args) {System.out.println("Collection: " +methodSet(Collection.class));interfaces(Collection.class);difference(Set.class, Collection.class);difference(HashSet.class, Set.class);difference(LinkedHashSet.class, HashSet.class);difference(TreeSet.class, Set.class);difference(List.class, Collection.class);difference(ArrayList.class, List.class);difference(LinkedList.class, List.class);difference(Queue.class, Collection.class);difference(PriorityQueue.class, Queue.class);System.out.println("Map: " + methodSet(Map.class));difference(HashMap.class, Map.class);difference(LinkedHashMap.class, HashMap.class);difference(SortedMap.class, Map.class);difference(TreeMap.class, Map.class);}
}

在这里插入图片描述

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

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

相关文章

5.网络之IP

IP协议&#xff08;网络层&#xff09; 文章目录 IP协议&#xff08;网络层&#xff09;1. 报文格式2. IP地址2. 地址管理3. 特殊IP地址 IP协议&#xff08;Internet Protocol&#xff0c;互联网协议&#xff09;&#xff0c;是TCP/IP协议栈中最核心的协议之一&#xff0c;通过…

大语言模型研究进展综述

1、历史 自20世纪50年代图灵测试被提出以来&#xff0c;研究人员一直在探索和开发能够理解并掌握语言的人工智能技术。 作为重要的研究方向之一&#xff0c;语言模型得到了学术界的广泛研究&#xff0c;从早期的统计语言模型和神经语言模型开始&#xff0c;发展到基于Transform…

Linux CentOS配置阿里云yum源

一&#xff1a;先备份文件&#xff0c;在配置失败时可以恢复 cd /etc/yum.repos.d mkdir back mv *.repo back 二&#xff1a;下载阿里云yum源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo wget -O /etc/yum.repos.d/epel.…

Linux下使用vscode编写Python项目

我此处是使用VScode远程连接的服务器&#xff0c;具体方法可看如下&#xff1a; 1、vscode中安装Python插件 按上面步骤安装好Python插件后&#xff0c;重启vscode&#xff1b; 2、选择Python解释器 创建Python项目结构&#xff1a; 按下F1&#xff0c;打开vscode命令栏&am…

zotero跨Windows设备数据同步(利用OneDrive、百度云同步空间等云服务)

zotero跨Windows设备数据同步&#xff08;利用OneDrive、百度云同步空间等云服务&#xff09; 特别注意 不能在多个设备同步使用zotero&#xff0c;否则会导致同步出现异常。 基本原理 同步zotero的数据&#xff0c;即同步全部的文献和笔记、高亮标记信息。而这两类数据分别…

Git介绍及使用

目录 一、Git 的基本概念 1. 仓库&#xff08;Repository&#xff09;: 仓库是存储代码的地方。可以通过 命令将本地文件夹初始化为 Git 仓库&#xff0c;并使用 命令从远程仓库克隆到本地 2. 分支&#xff08;Branch&#xff09;: 分支是指从主分支上创建出来的一个分支&…

QQ邮箱批量发送

场景 已有用户邮箱,需要批量对他们发送一些广告信息。 完整代码 # coding=gbk import smtplib import csv from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipartdef send_email(msg_from, passwd, msg_to_list, text_content)

【QT】QT自定义C++类

在使用Qt的ui设计时&#xff0c;Qt为我们提供了标准的类&#xff0c;但是在很多复杂工程中&#xff0c;标准的类并不能满足所有的需求&#xff0c;这时就需要我们自定义C类。 下面以自定义的QPushButton作一个很简单的例子。 先新建默认Qt Widgets Application项目 一、自定义…

机器学习中的嵌入:释放表征的威力

简介 机器学习通过使计算机能够从数据学习和做出预测来彻底改变了人工智能领域。机器学习的一个关键方面是数据的表示&#xff0c;因为表示形式的选择极大地影响了算法的性能和有效性。嵌入已成为机器学习中的一种强大技术&#xff0c;提供了一种捕获和编码数据点之间复杂关系的…

二维码智慧门牌管理系统全新升级:个性化配置,智能管理,让你的社区更安全!

文章目录 前言一、个性化配置功能的升级二、智能化管理和便捷性 前言 随着科技的飞速发展&#xff0c;智能化管理已经成为各个领域的标配。在社区管理方面&#xff0c;智能化的优势在便捷性、高效性和安全性方面得到了广泛认可。最近&#xff0c;二维码智慧门牌管理系统经过全…

SpringBoot整合JUnit

1.创建新项目 说明&#xff1a;创建springboot_04_junit项目&#xff0c;选择对应的版本。 2.接口类 说明&#xff1a;新建BookDao接口。 package com.forever.dao;public interface BookDao {public void save(); }3.实现类 说明&#xff1a; 新建BookDaoImpl实现类。 pa…

npm发布自己的包

npm发布自己的包 1. 首先在npm官网注册一个自己的账户(有账号的可以直接登录) 注册地址 2. 创建一个自己的项目(如果已有自己的项目, 跳过这一步) npm init -y3. 确认自己的npm下载源, 只能使用npm官方的地址 npm config get registry修改地址源 npm config set registr…

洛谷P1102 A-B数对 详细解析及AC代码

P1102 A-B数对 前言题目题目背景题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1 提示题目分析注意事项 代码经典二分&#xff08;O(nlgn)&#xff09;酷炫哈希&#xff08;O(n)&#xff09; 后话额外测试用例样例输入 #2样例输出 #2 王婆卖瓜 题目来源 前言 酷&…

DASCTF X CBCTF 2023|无畏者先行

前言 笔者没有参加此次比赛&#xff0c;由于团队后面会复现此次比赛&#xff0c;所以笔者在此进行复现记录。 EASYBOX 考点&#xff1a;命令执行&#xff1f; 栈溢出 附件给了 docker 环境&#xff0c;可以直接在本地复现&#xff0c;但是 docker 我不会调试&#xff0c;幸…

11.Z-Stack协议栈使用

f8wConfig.cfg文件 选择信道、设置PAN ID 选择信道 #define DEFAULT_CHANLIST 0x00000800 DEFAULT_CHANLIST 表明Zigbee模块要工作的网络&#xff0c;当有多个信道参数值进行或操作之后&#xff0c;把结果作为 DEFAULT_CHANLIST值 对于路由器、终端、协调器的意义&#xff1…

Spring boot集成sentinel限流服务

Sentinel集成文档 Sentinel控制台 Sentinel本身不支持持久化&#xff0c;项目通过下载源码改造后&#xff0c;将规则配置持久化进nacos中&#xff0c;sentinel重启后&#xff0c;配置不会丢失。 架构图&#xff1a; 改造步骤&#xff1a; 接着我们就要改造Sentinel的源码。…

U盘显示无媒体怎么办?方法很简单

当出现U盘无媒体情况时&#xff0c;您可以在磁盘管理工具中看到一个空白的磁盘框&#xff0c;并且在文件资源管理器中不会显示出来。那么&#xff0c;导致这种问题的原因是什么呢&#xff1f;我们又该怎么解决呢&#xff1f; 导致U盘无媒体的原因是什么&#xff1f; 当您遇到上…

SLAM从入门到精通(被忽视的基础图像处理)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 工业上用激光slam的多&#xff0c;用视觉slam的少&#xff0c;这是大家都知道的常识。毕竟对于工业来说&#xff0c;健壮和稳定是我们必须要考虑的…

Pytorch 快速参数权重初始化

定义一个函数&#xff1a; 这里比如要初始化2维卷积权重值&#xff0c;采用xaiver 数据分布&#xff0c;还有很多其他的数据分布可以探索 def weights_init(m):if isinstance(m, nn.Conv2d):xavier(m.weight.data)xavier(m.bias.data) 然后定义一个含2维卷积的网络&#xff…

Zinx框架-游戏服务器开发002:框架学习-按照三层结构模式重构测试代码+Tcp数据适配+时间轮定时器

文章目录 1 Zinx框架总览2 三层模式的分析3 三层重构原有的功能 - 头文件3.1 通道层Stdin和Stdout类3.1.2 StdInChannel3.1.2 StdOutChannel 3.2 协议层CmdCheck和CmdMsg类3.2.1 CmdCheck单例模式3.2.1.1 单例模式3.2.1.2 * 命令识别类向业务层不同类别做分发 3.2.2 CmdMsg自定…