《JAVA编程思想》学习笔记---第十五章:泛型

2,简单泛型

一个元组类库

return只能返回一个对象,要是想返回多个对象,解决方法就是让返回的这个对象持有多个想返回的对象,但是每次返回都需要不同类型的对象,那就需要创建很多类,使用泛型可以很好地避免此类问题。同时,我们在编译时就可以确保类型安全。
这种概念就成为元组。这个容器对象允许读取对象,但是允许向其中添加对象。

package com.generic;

public class TwoTuple<A,B> {

    public final A first;
    public final B second;
    public TwoTuple(A a,B b){
        first = a;
        second = b;
    }

    public String toString(){
        return "("+first+ "  "+second+")";
    }

}

这里变量设置为Final,可以保证其不被修改。

可以使用继承机制实现长度更长的元组:

package com.generic;

public class ThreeTuple<A,B,C> extends TwoTuple<A, B> {

    public final C third;
    public ThreeTuple(A a,B b,C c){
        super(a,b);
        third = c;
    }
    public String toString(){
        return "("+first+ "  "+second+"    "+third+")";
    }
}

实现更长的元祖以此类推。
使用元组:

package com.generic;

class Amp{}

public class TupleTest {

    static TwoTuple<String,Integer> f(){
        return new TwoTuple<String,Integer>("hi",47);
    }

    static ThreeTuple<Amp,String,Integer> g(){
        return new ThreeTuple(new Amp(),"three",48);
    }

    public static void main(String[] args) {

        System.out.println(f());
        System.out.println(g());
    }

}

一个堆栈类

不用Linkedlist的Stack:

package com.generic;

public class LinkedStack<T> {

    private static class Node<U>{
        U item;
        Node<U> next;
        Node(){
            item = null;
            next = null;
        }
        Node(U item, Node<U> next){
            this.item = item;
            this.next = next;
        }
        boolean end(){
            return item == null&&next == null;
        }
    }
    private Node<T> top = new Node<T>();

    public void push(T item){
        top = new Node<T>(item,top);
    }

    public T pop(){
        T result = top.item;
        if(!top.end()){
            top = top.next;
        }
        return result;
    }

    public static void main(String[] args) {
        LinkedStack<String> lss = new LinkedStack<>();
        for(String s:"Phasers on stun!".split(" ")){
            lss.push(s);
        }
        String s;
        while((s = lss.pop()) != null){
            System.out.println(s);
        }
    }

}
/*
输出:
stun!
on
Phasers
*/

很有意思的例子。
个人理解相当于一个链表的样子。

RandomList

一个使用泛型实现随机访问的例子:

package com.generic;

import java.util.ArrayList;
import java.util.Random;

public class RandomList<T> {

    private ArrayList<T> storage = new ArrayList<T>();
    private Random rand = new Random(47);

    public void add(T item){
        storage.add(item);
    }

    public T select(){
        return storage.get(rand.nextInt(storage.size()));
    }

    public static void main(String[] args) {

        RandomList<String> rs = new RandomList<>();
        for(String s:"the quick brown fox jumped over".split(" ")){
            rs.add(s);
        }
        for(int i = 0; i < 5; i++){
            System.out.print(rs.select()+" ");
        }
    }

}
/*
输出:
brown over quick over quick 
*/

3,泛型接口

package com.generic;

public interface generator<T> {

    T next() throws InstantiationException, IllegalAccessException;
}

定义一些Coffee类:

package com.generic;

public class Coffee {

    private static long counter = 9;
    private final long id = counter++;
    public String toString(){
        return getClass().getSimpleName();
    }
}

public class Latte extends Coffee{}

....

编写类,实现这个接口,随机生成不同类型的Coffee对象:

package com.generic;

import java.util.*;

public class CoffeeGenerator implements generator<Coffee>,Iterable<Coffee>{

    private Class[] types = {Latte.class,Mocha.class,Cappuccino.class,Americano.class,Breve.class};
    private static Random rand = new Random(47);
    public CoffeeGenerator(){}
    private int size = 0;
    public CoffeeGenerator(int sz){
        size = sz;
    }
    @Override
    public Coffee next() throws InstantiationException, IllegalAccessException {
        return (Coffee)types[rand.nextInt(types.length)].newInstance();
    }

    class CoffeeIterator implements Iterator<Coffee>{

        int count = size;
        @Override
        public boolean hasNext() {
            return count > 0;
        }

        @Override
        public Coffee next() {
            count--;
            Coffee coffee = null;
            try {
                coffee = CoffeeGenerator.this.next();
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return coffee;
        }

    }

    @Override
    public Iterator<Coffee> iterator() {
        return new CoffeeIterator();
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException{
        CoffeeGenerator gen = new CoffeeGenerator();
        for(int i = 0; i < 5; i++){
            System.out.print(gen.next()+"   ");
        }
        System.out.println();
        for(Coffee c: new CoffeeGenerator(5)){
            System.out.print(c+"   ");
        }
    }
}
/*
输出:
Americano   Latte   Americano   Mocha   Mocha   
Breve   Americano   Latte   Cappuccino   Cappuccino   
*/

接口泛型的另一个实例:

package com.generic;

import java.util.Iterator;

public class Fibonacci implements generator<Integer>,Iterable<Integer>{

    private int count = 0;
    private int n = 0;
    public Fibonacci(){}
    public Fibonacci(int n){
        this.n = n;
    }
    @Override
    public Integer next() throws InstantiationException, IllegalAccessException {
        return fib(count++);
    }
    private int fib(int n){
        if(n < 2){
            return 1;
        }
        return fib(n-2)+fib(n-1);
    }

    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>(){

            @Override
            public boolean hasNext() {
                return n > 0;
            }

            @Override
            public Integer next() {
                n--;
                int i = 0;
                try {
                    i =  Fibonacci.this.next();
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                return i;
            }

        };
    }

    public static void main(String[] args) throws InstantiationException, IllegalAccessException{
        Fibonacci gen = new Fibonacci();
        for(int i = 0;i < 18;i++){
            System.out.print(gen.next()+"  ");
        }
        System.out.println();
        for(int i: new Fibonacci(18)){
            System.out.print(i+"  ");
        }
    }


}
/*
输出:
1  1  2  3  5  8  13  21  34  55  89  144  233  377  610  987  1597  2584  
1  1  2  3  5  8  13  21  34  55  89  144  233  377  610  987  1597  2584 
*/

4,泛型方法

package com.generic;

public class GenericMethods {

    public static <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.0);
        gm.f(1.0f);
        gm.f('c');
        gm.f(gm);
        f("");
    }

}

泛型方法可以不依赖与泛型类,如果使用泛型方法可以取代泛型类,那就尽量使用泛型方法!
这种利用参数判断泛型类型。下面使用返回值判断泛型类型。

类型参数推断

package com.generic;

import java.util.*;

public class New {

    public static <K,V> Map<K,V> map(){
        return new HashMap<K,V>();
    }

    public static <T> void list(){

    }

    public static <T> LinkedList<T> lList(){
        return new LinkedList<T>();
    }

    public static <T> Set<T> set(){
        return new HashSet<T>();
    }

    public static <T> Queue<T> queue(){
        return new LinkedList<T>();
    }

    public static void main(String[] args) {
        Map<String,List<String>> sls = New.map();
        New.list();
        LinkedList<String> lls = New.lList();
        Set<String> ss = New.set();
        Queue<String> qs = New.queue();
    }

}

但是这种方法只在赋值操作时有效,但在其他地方并不适用,看如下示例:

package com.generic;

import java.util.*;

public class LimitsOfInference {

    static void f(Map<Coffee,Coffee> coffee){
        System.out.println(coffee.getClass());
        coffee.put(new Coffee(), new Latte());
    }
    public static void main(String[] args) {
        f(New.map());
    }

}

这段代码书上说不可以,但是实际是可以的!
可以显示的指明类型:

package com.generic;

import java.util.Map;

public class ExplicitTypeSpecification {

    static void f(Map<Coffee,Coffee> coffee){
        System.out.println(coffee.getClass());
        coffee.put(new Coffee(), new Latte());
    }

    public static void main(String[] args) {
        f(New.<Coffee,Coffee>map());
    }

}

这种必须在方法前指明调用者,在方法前指明泛型类型,这种方法不常用。

可变参数与泛型方法

package com.generic;

import java.util.*;

public class GenericVarargs {

    public static <T> List<T> makeList(T...args){
        List<T> result = new ArrayList<T>();
        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);
    }

}

用于Generator的泛型方法

就是一个使用泛型的例子:

package com.generic;

import java.util.*;

public class Generators {

    public static <T> Collection<T> 
    fill(Collection<T> coll,generator<T> gen,int n) throws InstantiationException, IllegalAccessException{
        for(int i = 0; i < n;i++){
            coll.add(gen.next());
        }
        return coll;
    }
    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        Collection<Coffee> coffee = fill(new ArrayList<Coffee>(),new CoffeeGenerator(),4);
        for(Coffee c:coffee){
            System.out.println(c);
        }

    }
}

一个通用的Generator

package com.generic;

public class BasicGeneratorDemo {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
        for(int i = 0; i < 5; i++){
            System.out.println(gen.next());
        }
    }

}
package com.generic;

public class CountedObject {

    private static long counter = 0;
    private final long id = counter++;
    public long id(){
        return id;
    }
    public String toString(){
        return "CountedObject"+id;
    }
}
package com.generic;

public class BasicGeneratorDemo {

    public static void main(String[] args) throws InstantiationException, IllegalAccessException {
        generator<CountedObject> gen = BasicGenerator.create(CountedObject.class);
        for(int i = 0; i < 5; i++){
            System.out.println(gen.next());
        }
    }

}

挺简单的,不解释了。

简化元组的使用

package com.generic;

public class Tuple {

    public static <A,B> TwoTuple<A,B> tuple(A a, B b){
        return new TwoTuple<A,B>(a,b);
    }

    public static <A,B,C> ThreeTuple<A,B,C> tuple(A a, B b,C c){
        return new ThreeTuple<A,B,C>(a,b,c);
    }
}
package com.generic;

import static com.generic.Tuple.*;

public class TupleTest2 {

    static TwoTuple<String,Integer> f(){
        return tuple("hi",47);
    }

    static TwoTuple f2(){
        return tuple("hi",47);
    }
    static ThreeTuple<Amphibian,String,Integer> h(){
        return tuple(new Amphibian(),"hi",47);
    }
    public static void main(String[] args) {
        System.out.println(f());
        System.out.println(f2());
        System.out.println(h());
    }

}

一个Set实用工具

有一个例子,略!

5,匿名内部类

泛型的一个好处就是可以简单而安全的创建复杂的模型,下面实例很容易的创建了List元组:

package com.generic;

import java.util.*;

public class TupleList<A,B,C> extends ArrayList<ThreeTuple<A,B,C>> {
    public static void main(String[] args){
        TupleList<Amphibian,String,Integer> t1 = new TupleList<>();
        t1.add(TupleTest.g());
        t1.add(TupleTest.g());
        for(ThreeTuple<Amphibian,String,Integer> i : t1){
            System.out.println(i);
        }
    }
}

另一个复杂模型,真够复杂的,有点搞不懂:

package com.generic;

import java.util.*;

class Product{
    private final int id;
    private String description;
    private double price;
    public Product(int IDumber,String descr,double price){
        id = IDumber;
        description = descr;
        this.price = price;
        System.out.println(toString());
    }
    public String toString(){
        return id+":"+description+".price:$"+price;
    }
    public void priceChange(double change){
        price += change;
    }
    public static generator<Product> generator = new generator<Product>(){
        private Random rand = new Random(47);
        public Product next(){
            return new Product(rand.nextInt(1000),"Test",
                    Math.round(rand.nextDouble()*1000.0)+0.99);
        }
    };
}

class Shelf extends ArrayList<Product>{
    public Shelf(int nProducts){
        try {
            Generators.fill(this, Product.generator, nProducts);
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

class Aisle extends ArrayList<Shelf>{
    public Aisle(int nShelves,int nProducts){
        for(int i = 0; i < nShelves;i++){
            add(new Shelf(nProducts));
        }
    }
}

class CheckoutStand{}
class Office{}

public class Store extends ArrayList<Aisle>{

    private ArrayList<CheckoutStand> checkouts = new ArrayList<CheckoutStand>();
    private Office office = new Office();
    public Store(int nAisles, int nShelves,int nProducts){
        for(int i = 0; i < nAisles;i++){
            add(new Aisle(nShelves,nProducts));
        }
    }
    public String toString(){
        StringBuilder result = new StringBuilder();
        for(Aisle a : this){
            for(Shelf s: a){
                for(Product p : s){
                    result.append(p);
                    result.append("\n");
                }
            }
        }
        return result.toString();
    }
    public static void main(String[] args){
        System.out.println(new Store(14,5,10));
    }
}

逻辑有点复杂,智商不够用。

7,擦除的神秘之处

尽管可以声明ArrayList.class但是不能声明ArrayList<Integer>

package com.generic;

import java.util.*;

public class ErasedTypeEquivalence {

    public static void main(String[] args) {
        Class c1 = new ArrayList<String>().getClass();
        Class c2 = new ArrayList<Integer>().getClass();
        System.out.println(c1 == c2);
    }

}
/*
输出:
true
*/

利用反射得到泛型信息:

package com.generic;

import java.util.*;

class Frob{}
class Fnorkle{}
class Quark<Q>{}
class Particle<POSITION,MOMENTUM>{}
public class LostInformation {

    public static void main(String[] args) {
        List<Frob> list = new ArrayList<>();
        Map<Frob,Fnorkle> map = new HashMap<>();
        Quark<Fnorkle> quark = new Quark<>();
        Particle<Long,Double> p = new Particle<>();
        System.out.println(Arrays.toString(list.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(map.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(quark.getClass().getTypeParameters()));
        System.out.println(Arrays.toString(p.getClass().getTypeParameters()));
    }

}
/*
输出:
[E]
[K, V]
[Q]
[POSITION, MOMENTUM]
*/

输出的只是用作参数的占位符的表示符。
因此,残酷的现实是:
在泛型代码内部,无法获得任何有关泛型参数类型的信息
java泛型是使用擦除来实现的,这意味着使用泛型时,任何具体的类型信息都被擦除了!

C++的方式

看不懂,不看

迁移兼容性

好多字,大体的意思就是泛型不是在java出现时就有的特性,而是后来添加的。为了不使之前的类库代码报废,只好使用了向前兼容的方式,所有使用了擦除的方式。

擦除的问题

擦除的代价是显著的,泛型不能用于显示的引用运行时类型的操作之中,例如转型,instanceof,和new表达式。因为所有关于参数的类型的信息都丢失了,所以无论何时,在编写泛型代码时,必须时刻提醒自己,这只是看起来拥有有关参数的类型信息而已,因此,如果看到如下代码:

class Foo<T>{
    T var;
}

创建实例时:

Foo<Cat> f = new Foo<Cat>();

Foo类的代码应该知道工作在Cat类之上,而泛型语法也强烈暗示,在整个类的所有地方,类型T都被Cat替换。但是事实并非如此,当在编写这个类的代码时,必须提醒自己:“这只是一个Object”;

所以,擦除和迁移兼容性以为这使用泛型并不是强制的,不使用泛型只会出现警告而不是报错!

package com.generic;

class GenericBase<T>{
    private T element;
    public void set(T arg){
        element = arg;
        System.out.println("set");
    }
    public T get(){
        return element;
    }
}

class Derived1<T> extends GenericBase<T>{}
class Derived2 extends GenericBase{}//不警告

public class ErasureAndInheritance {

    public static void main(String[] args) {
        Derived2 d2 = new Derived2();
        Object obj = d2.get();
        d2.set(obj);//警告
    }

}

边界处的动作

正式有了擦除,泛型可以表示没有任何意义的实物:

package com.generic;

import java.lang.reflect.Array;
import java.util.Arrays;

public class ArrayMaker<T> {

    private Class<T> kind;

    public ArrayMaker(Class<T> kind){
        this.kind = kind;
    }

    T[] create(int size){
        //T[] t = new T[]{kind.newInstance(),kind.newInstance()};报错
        return (T[])Array.newInstance(kind, size);
    }

    public static void main(String[] args) {
        ArrayMaker<String> stringMaker = new ArrayMaker<>(String.class);
        String[] stringArray = stringMaker.create(9);
        System.out.println(Arrays.toString(stringArray));
    }

}
/*
输出:
[null, null, null, null, null, null, null, null, null]
*/

注意,在泛型中,推荐使用Array.newInstance()。
创建一个容器而不是一个数组:

package com.generic;

import java.util.*;

public class FilledListMaker<T> {

    List<T> create(T t, int n){
        List<T> result = new ArrayList<T>();
        for(int i = 0; i <n;i++){
            result.add(t);
        }
        return result;
    }

    public static void main(String[] args) {
        FilledListMaker<String> stringMaker = new FilledListMaker<>();
        List<String> list = stringMaker.create("Hello", 4);
        System.out.println(list);
    }

}

擦除的补偿

擦除丢失了在泛型代码中执行某些操作的能力。任何在运行时需要知道确切类型信息的操作都无法总做!

package com.generic;

public class Erased<T> {

    private final int SIZE = 100;
    public  void f(Object arg){
        /*if(arg instanceof T){

        }
        T var = new T();
        T[] array = new T[SIZE];*///报错
        T[] array  = (T[])new Object[SIZE];//警告
    }
}

绕过这些问题来编程,可以使用Class:

package com.generic;

class Building{}
class House extends Building{}

public class ClassTypeCapture<T> {

    Class<T> kind;

    public ClassTypeCapture(Class<T> kind){
        this.kind = kind;
    }

    public boolean f(Object arg){
        return kind.isInstance(arg);
    }

    public static void main(String[] args) {
        ClassTypeCapture<Building> ctt = new ClassTypeCapture<>(Building.class);
        System.out.println(ctt.f(new Building()));
        System.out.println(ctt.f(new House()));
        ClassTypeCapture<House> ctt2 = new ClassTypeCapture<>(House.class);
        System.out.println(ctt2.f(new Building()));
        System.out.println(ctt2.f(new House()));
    }

}

这样,可以确保类型标签可以匹配泛型参数。

个人理解:
泛型 T 只可以用来标识另一种东西,如容器类型,返回值类型,参数类型等,但不可以像类一样生成对象!即不能用在运行时。

new T[]这样的操作无法实现部分原因是因为擦除,而另一部分原因是因为编译器不能检查T具有默认构造器这里写代码片,下面代码将解决这一问题:

package com.generic;

class ClassAsFactory<T>{
    public T x;
    public ClassAsFactory(Class<T> kind){
        try {
            x = kind.newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }
}

class Employee{
    public void print(){
        System.out.println("Employee");
    }
}

public class InstantiateGenericType {

    public static void main(String[] args) {
        ClassAsFactory<Employee> fe = new ClassAsFactory<>(Employee.class);
        fe.x.print();
        ClassAsFactory<Integer> in = new ClassAsFactory<>(Integer.class);

    }

}

可以看到生成ClassAsFactory<Integer>实例时会报错,因为Integer没有默认的构造器。同时,这个错误不是在编译期捕获的。

使用显示的工厂方法避免运行时异常,个人认为这种方法不太灵活。

package com.generic;

interface Factory<T>{
    T create();
}

class Foo2<T>{
    private T x;
    public <F extends Factory<T>> Foo2(F factory){
        x = factory.create();
    }
}

class IntegerFactory implements Factory<Integer>{
    public Integer create(){
        return new Integer(0);
    }
}

class Widght{
    public static class FactoryDemo implements Factory<Widght>{
        public Widght create(){
            return new Widght();
        }
    }
}

public class FactoryConstraint {

    public static void main(String[] args) {
        new Foo2<Integer>(new IntegerFactory());
        new Foo2<Widght>(new Widght.FactoryDemo());
    }

}

使用模板方法模式也能解决这个问题:

package com.generic;

abstract class GenericWithCreate<T>{
    final T ele;
    GenericWithCreate(){
        ele = create();
    }
    abstract T create();
}

class X{}
class Creator extends GenericWithCreate<X>{
    X create(){
        return new X();
    }
    void f(){
        System.out.println(ele.getClass().getSimpleName());
    }
}

public class CreatorGeneric {

    public static void main(String[] args) {
        Creator creator = new Creator();
        creator.f();
    }

}

泛型数组

如前面所示,不能创建泛型数组一般的解决档案是使用ArrayList:

package com.generic;

import java.util.*;

public class ArrayOfGenericReference<T> {

    private List<T> array = new ArrayList<T>();
    public void add(T item){
        array.add(item);
    }
    public T get(int index){
        return array.get(index);
    }
}
package com.generic;

import java.lang.reflect.Array;

class Generic<T>{}
public class ArrayOfGeneric {

    static final int SIZE = 5;
    static Generic<Integer>[] gia;
    public static void main(String[] args) {
        gia = (Generic<Integer>[])new Generic[SIZE];
        System.out.println(gia.getClass().getSimpleName());
        gia[0] = new Generic<Integer>();
        //gia[1] = new Generic<Double>();报错
        for(int i = 0; i  < SIZE; i++){
            System.out.println(gia[i]);
        }
    }

}

挺正常的代码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值